Manchmal weiß man erst zur Laufzeit, welche Komponente gerendert werden soll — bei Wizard-Schritten, Tab-Inhalten, Plugin-Strukturen oder Editor-Layouts. In Svelte 5 funktioniert das ohne <svelte:component> — eine Komponenten-Variable kann direkt als Tag verwendet werden, solange sie mit Großbuchstaben beginnt. Dieser Artikel zeigt die typischen Patterns und die Migration aus Svelte 4.

Die einfache Form

Eine Variable, die eine Komponente enthält, lässt sich direkt als Tag setzen — vorausgesetzt sie beginnt mit Großbuchstaben:

svelte Tab-Demo
<script>
    import TabHome from './TabHome.svelte';
    import TabProfile from './TabProfile.svelte';
    import TabSettings from './TabSettings.svelte';

    let active = $state(TabHome);
</script>

<nav>
    <button onclick={() => active = TabHome}>Home</button>
    <button onclick={() => active = TabProfile}>Profil</button>
    <button onclick={() => active = TabSettings}>Einstellungen</button>
</nav>

<active />

<active /> rendert die Komponente, die aktuell in active gespeichert ist. Bei jedem Wechsel von active rendert Svelte die alte Komponente weg und die neue auf.

Mit Props

Dynamische Komponenten nehmen Props wie jeder andere Tag:

svelte Mit Props
<script>
    let { Component, ...rest } = $props();
</script>

<Component {...rest} />

Eine generische Render-Hülle, die ihre eigentliche Komponente per Prop bekommt.

Map-basiertes Pattern

Wenn die Komponentenwahl auf einem String-Schlüssel basiert (z. B. type aus einem JSON), eignet sich eine Map:

svelte Block-Renderer
<script>
    import TextBlock from './blocks/TextBlock.svelte';
    import ImageBlock from './blocks/ImageBlock.svelte';
    import VideoBlock from './blocks/VideoBlock.svelte';

    const components = {
        text: TextBlock,
        image: ImageBlock,
        video: VideoBlock,
    };

    let { blocks } = $props();
</script>

{#each blocks as block (block.id)}
    {@const Component = components[block.type]}
    {#if Component}
        <Component {...block} />
    {:else}
        <p>Unbekannter Block: {block.type}</p>
    {/if}
{/each}

Die Konstante muss mit Großbuchstaben beginnen, damit Svelte sie als Komponente erkennt — daher Component (nicht component).

Lazy-Loading dynamischer Komponenten

Mit dynamischem Import lassen sich Komponenten erst dann laden, wenn sie gebraucht werden — das spart initiale Bundle-Größe:

svelte Lazy-Component-Loading
<script>
    let Component = $state(null);

    async function loadEditor() {
        const mod = await import('$lib/components/HeavyEditor.svelte');
        Component = mod.default;
    }
</script>

{#if Component}
    <Component />
{:else}
    <button onclick={loadEditor}>Editor laden</button>
{/if}

Auch eine Variante mit {#await}-Block:

svelte await-basierte Variante
<script>
    let promise = $state(null);

    function load() {
        promise = import('$lib/components/HeavyEditor.svelte');
    }
</script>

<button onclick={load}>Editor laden</button>

{#if promise}
    {#await promise}
        <p>lädt …</p>
    {:then mod}
        {@const Editor = mod.default}
        <Editor />
    {/await}
{/if}

Migration von <svelte:component>

In Svelte 4 brauchte es ein spezielles Tag, um eine Komponente per Variable zu rendern:

svelte Svelte 4
<svelte:component this={CurrentTab} {...tabProps} />

In Svelte 5 ist das überflüssig — direkt als Tag schreiben:

svelte Svelte 5
<CurrentTab {...tabProps} />

<svelte:component> funktioniert weiterhin im Legacy-Modus, ist aber nicht mehr nötig.

Praxis-Beispiel: Multi-Step-Wizard

svelte Wizard.svelte
<script>
    import StepIntro from './steps/StepIntro.svelte';
    import StepDetails from './steps/StepDetails.svelte';
    import StepConfirm from './steps/StepConfirm.svelte';

    const steps = [
        { Component: StepIntro, title: 'Einstieg' },
        { Component: StepDetails, title: 'Details' },
        { Component: StepConfirm, title: 'Bestätigung' },
    ];

    let stepIndex = $state(0);
    let current = $derived(steps[stepIndex]);

    let formData = $state({});
</script>

<header>
    <h2>{current.title}</h2>
    <progress value={stepIndex + 1} max={steps.length}></progress>
</header>

<current.Component bind:data={formData} />

<nav>
    {#if stepIndex > 0}
        <button onclick={() => stepIndex--}>Zurück</button>
    {/if}
    {#if stepIndex < steps.length - 1}
        <button onclick={() => stepIndex++}>Weiter</button>
    {/if}
</nav>

Beachten: Beim Property-Zugriff bleibt der Compiler streng — nur Top-Level-Bezeichner mit Großbuchstaben werden erkannt. Bei current.Component muss daher das Property selbst mit Großbuchstaben geschrieben werden.

Häufige Stolperfallen

Variable kleingeschrieben. <myComponent /> interpretiert Svelte als HTML-Element — kein Fehler, aber auch keine Komponente.

Property-Zugriff mit Kleinschreibung. <current.component /> funktioniert nicht. <current.Component /> ja. Konvention also: das Property-Feld muss großgeschrieben sein.

Wechsel ohne State-Reset. Wenn beim Wechsel der Komponente State erhalten bleiben soll, muss er außerhalb der Komponente liegen — z. B. in einem Eltern-Komponent oder in einem Store. Andernfalls wird beim Komponenten-Wechsel die Instanz zerstört und mit ihr der State.

Inhalte nicht reagieren auf Wechsel. Wenn Snippets oder Children verwendet werden, müssen sie ggf. key-basiert neu erstellt werden, damit Lifecycle-Hooks korrekt feuern.

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Components

Zur Übersicht