Dynamically load a template in a Svelte component

Viewed 59

I'm not entirely sure if this is possible, but I want to dynamically load a component's template in runtime in Svelte. Basically download some user-defined template and use that to build out the component in run-time. Is this actually possible to do?

1 Answers

You would have to include the Svelte compiler and use that to compile the template. You could check out how the REPL's repository sets that up.

The REPL performs two major steps:

  • Convert the code to a raw component which still references 'svelte/internal'
  • Bundle the component so all referenced code is included via Rollup

The first step is easy, you just do something like this:

import { compile } from 'svelte/compiler';

const { js, css } = compile(template, {
  filename: 'Component.svelte',
  format: 'esm',
});

console.log(js.code, css.code);

The bundling is a bit more involved (see its worker's code). You could however, make the internals available for the import and just run the code directly.

E.g. using unpkg.com for testing and throwing the code into an iframe.srcdoc:

const code = `
    ${js.code.replace(
        'svelte/internal',
        'https://unpkg.com/svelte@3.49.0/internal/index.mjs',
    )}

    new Component({ target: document.body });
`;

srcdoc = `
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>Svelte App</title>
            <${''}style>${css.code}</${''}style>
        </head>
        <body>
            <${''}script type="module">${code}</${''}script>
        </body>
    </html>
`;

(The tags are split using ${} because the Svelte parser will break otherwise.)

You could also insert the code directly into your document via a new module script, you just have to identify the target element somehow, e.g. by generating a unique random ID.

Here is something neat you can do to directly use the generated code: Dynamically import the script from a Blob.

const componentSrc = URL.createObjectURL(
    new Blob([code], { type: 'text/javascript' })
);
const { default: Component } = await import(componentSrc);
URL.revokeObjectURL(componentSrc);

new Component({ target: ... });

If you use the component outside a sandboxed iframe, you should be aware of the XSS risk that you expose yourself to.

Related