Enzo Innocenzi

Replacing parts of a string with Vue components

Someone in the Inertia.js Discord server recently asked for a way to replace parts of an input string with custom Vue components:

I had never done that, but thought it would be fun to find out how to do it. Turns out, you can't simply replace your placeholder with some HTML that calls a Vue component:

const input = 'This is some text with a __placeholder__'
const html = input.replace('__placeholder__', '<custom-component></custom-component>')

So I looked into it deeper, and found out that the correct way to do this is to use a render function. For the example above, the corresponding render function would be:

import { h } from 'vue'
import Placeholder from './placeholder.vue'

function render() {
    return [
        'This is some text with a ',
        h(Placeholder)
    ]
}

This is pretty simple, but we need to find a way to split the input string by multiple separators, and replace these separators with the render function. Since I'm lazy, I asked ChatGPT to do it for me:

Apparently, ChatGPT perfectly understood my gibberish prompt and gave me a fully working function. After cleaning it up a bit, the rest is a matter of mapping the result of this function to the components we want to replace:

<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'

const input = 'This __foo__ and this __bar__ are Vue components'
const replacements = {
    __foo__: () => h(Foo),
    __bar__: () => h(Bar),
}

function splitByArray(inputString, splitArray) {
    return splitArray.reduce((result, splitString) => {
        return result.reduce((newResult, str) => {
            const parts = str.split(splitString)
            for (let i = 0; i < parts.length - 1; i++) {
                newResult.push(parts[i], splitString)
            }
            newResult.push(parts[parts.length - 1])
            return newResult
        }, [])
    }, [inputString])
}

function render() {
    return splitByArray(input, Object.keys(replacements).map((part) => {
        return replacement[part]?.() ?? part
    }))
}
</script>

<template>
    <component :is="render" />
</template>
Made with by   Enzo Innocenzi