A powerful, secure, and flexible runtime component compiler for Svelte 5+ applications.
- Runtime Component Compilation: Transform Svelte component strings into fully functional components on the fly.
- Type Safety: Comprehensive TypeScript support with detailed interfaces and type definitions.
- Svelte 5 Ready: Full support for Svelte 5 runes and the latest features.
npm install @mateothegreat/dynamic-component-engine svelteFirst, you need to update your index.html to include the following importmap:
<script type="importmap">
{
"imports": {
"svelte": "https://esm.sh/[email protected]",
"svelte/internal/disclose-version": "https://esm.sh/[email protected]/internal/disclose-version",
"svelte/internal/": "https://esm.sh/[email protected]/internal/"
}
}
</script>Note
This is necessary to ensure that the Svelte runtime is available to the dynamic component engine. See index.html for a working implementation.
Here's an example of how to use the dynamic component engine to render a component from a remote source (in this case, a locally compiled Svelte component served by vite):
<script lang="ts">
import { onDestroy, onMount } from "svelte";
import {
load,
render,
type Rendered
} from "@mateothegreat/dynamic-component-engine";
let renderRef: HTMLDivElement;
let component: Rendered;
async function create() {
try {
const source = await fetch(
"https://dynamic-component-engine.matthewdavis.io/entry.js"
).then((res) => res.text());
const fn = await load(source);
component = await render(fn, {
componentSource: source,
target: renderRef,
props: {
foo: "bar"
}
});
} catch (error) {
console.error(error);
}
}
onMount(async () => {
create();
});
onDestroy(() => {
component.destroy();
});
</script>
<div bind:this={renderRef}></div>You must first compile your Svelte component(s) down to a string and serve it to the client (http endpoint, websockets, etc.) then you can use the load and render functions to dynamically render the component(s) in the browser.
import esbuild from "esbuild";
import esbuildSvelte from "esbuild-svelte";
import { sveltePreprocess } from "svelte-preprocess";
async function bundleSvelte(entry) {
const build = await esbuild.build({
logLevel: "debug",
entryPoints: Array.isArray(entry) ? entry : [entry],
target: "esnext",
format: "esm",
splitting: false,
packages: "external",
banner: {
js: "// I'm compiled from entry.ts which imports simple.svelte using esbuild-svelte."
},
bundle: true,
outdir: "./public",
plugins: [
esbuildSvelte({
preprocess: sveltePreprocess()
})
]
});
return build.outputFiles;
}
bundleSvelte(["./src/components/entry.ts"]);Now you're ready to compile your svelte component(s) down to an esm module:
node build.jsShould show something like this in your terminal:
$ node build.js
public/entry.js 1.2kb
⚡ Done in 156msAfter running node build.js the output will be a single file in the public directory and will look like this:
const compiledComponentSource = `
// I'm compiled from entry.ts which imports simple.svelte using esbuild-svelte.
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// src/components/entry.ts
import { mount, unmount } from "svelte";
// src/components/simple.svelte
import "svelte/internal/disclose-version";
import * as $ from "svelte/internal/client";
var on_click = /* @__PURE__ */ __name((_, testState) => $.update(testState), "on_click");
var root = $.from_html(`<div></div> <button> </button>`, 1);
function Simple($$anchor) {
let name = "I'm but a simple component";
let testState = $.state(0);
var fragment = root();
var div = $.first_child(fragment);
var button = $.sibling(div, 2);
button.__click = [on_click, testState];
var text = $.child(button, true);
$.reset(button);
$.template_effect(() => $.set_text(text, $.get(testState)));
$.append($$anchor, fragment);
}
__name(Simple, "Simple");
$.delegate(["click"]);
// src/components/entry.ts
var factory = /* @__PURE__ */ __name((target, props) => {
const component = mount(Simple, { target, props });
return {
component,
name: Simple.name,
destroy: /* @__PURE__ */ __name(() => {
console.log("entry.ts -> simple.svelte", "destroying component", component);
unmount(component);
}, "destroy")
};
}, "factory");
export {
factory as default
};
`;We welcome contributions! Whether it's bug reports, feature requests, or pull requests, all contributions are appreciated.
MIT License - feel free to use in personal and commercial projects.
Built with ❤️ from Texas for the Svelte community.