🍡 mochi
← All demos

Nested Components

A single Level.svelte renders itself recursively five deep — the next child held in $state, dispatched as <Next />. Hydrating just the root carries the entire subtree across; every button at every depth works.

mochi:hydrate on the root

One island wraps the whole five-level tree. Mochi serializes the root and its descendants once, ships a single bundle, and Svelte hydrates the entire subtree together. Every button at every depth is interactive.

Level 1 (root) SSR only
Level 2 SSR only
Level 3 SSR only
Level 4 SSR only
Level 5 (leaf) SSR only

mochi:defer + mochi:hydrate

The same recursive tree, rendered lazily as a server island. The placeholder ships with the page; the browser fetches the rendered HTML on demand. Because mochi:hydrate is also set, Svelte takes over once the fetched markup lands — same five interactive levels, just delivered later.

Loading nested tree from server
<script>
  import Level from './Level.svelte';
</script>

<section class="mode">
  <header>
    <h2><code>mochi:hydrate</code> on the root</h2>
    <p>
      One island wraps the whole five-level tree. Mochi serializes the root and its descendants once, ships a single bundle, and Svelte hydrates the entire subtree together.
      Every button at every depth is interactive.
    </p>
  </header>
  <Level mochi:hydrate />
</section>

<section class="mode">
  <header>
    <h2><code>mochi:defer</code> + <code>mochi:hydrate</code></h2>
    <p>
      The same recursive tree, rendered lazily as a server island. The placeholder ships with the page; the browser fetches the rendered HTML on demand. Because <code
        >mochi:hydrate</code
      > is also set, Svelte takes over once the fetched markup lands — same five interactive levels, just delivered later.
    </p>
  </header>
  <Level mochi:defer mochi:hydrate>
    <div class="placeholder">Loading nested tree from server<span class="dots"></span></div>
  </Level>
</section>
Styles
<style>
  .mode {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-bottom: 2.5rem;
  }

  .mode header {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
  }

  .mode h2 {
    margin: 0;
    font-size: 1.05rem;
  }

  .mode h2 code,
  .mode p code {
    font-family: var(--font-mono);
    background: var(--surface-muted);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 0.1em 0.35em;
    border-radius: 4px;
    font-size: 0.85em;
    font-weight: 500;
  }

  .mode p {
    margin: 0;
    color: var(--text-muted);
    font-size: 0.95rem;
    line-height: 1.5;
  }

  .placeholder {
    padding: 1rem;
    border: 2px dashed var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--surface-muted);
    color: var(--text-subtle);
    font-style: italic;
    text-align: center;
  }

  .dots::after {
    content: '';
    display: inline-block;
    width: 1.5em;
    text-align: left;
    animation: dots 1.2s steps(4, end) infinite;
  }

  @keyframes dots {
    0% {
      content: '';
    }
    25% {
      content: '.';
    }
    50% {
      content: '..';
    }
    75% {
      content: '...';
    }
  }
</style>