🍡 mochi
← All demos

Font loading

Ship a custom font with a Mochi page: as an @fontsource package, or a standalone .woff2 file referenced from a colocated CSS file. The same import works inside a hydratable island — the bundle stays in the page <head>, never in the JS. All go through the framework's CSS-import bundler with no shell.html changes.

Fontsource package

Add @fontsource/jetbrains-mono as a dependency and side-effect-import it from any component. The framework bundles the package's CSS (with woff2 inlined as data URIs), serves it from /_mochi/import-css/*.css, and links it from every page that transitively imports the component.

The quick brown fox jumps over the lazy dog. 1234567890

Standalone .woff2

Drop a .woff2 next to your component and reference it from a tiny @font-face CSS file:

@font-face {
  font-family: 'Lobster';
  src: url('./lobster.woff2') format('woff2');
}

Side-effect-import the CSS from the component (import './lobster.css'). Bun's CSS bundler inlines the .woff2 as a base64 data URI in the bundled CSS, so the font ships in the same request as the stylesheet — no separate font fetch.

The quick brown fox jumps over the lazy dog. 1234567890

Inside a hydratable island

The same import works in a component with mochi:hydrate. The framework strips the CSS import from both the SSR and the client bundle, then links the bundled stylesheet from the page <head>. The badge flips to hydrated in the browser; the styled text uses the bundled font on first paint with no JS-injected styles.

Hydrated island server-rendered only

Lobster Two ships with this island.

<script>
  import '@fontsource/jetbrains-mono';
  import './lobster.css';
  import HydratedBox from './HydratedBox.svelte';
</script>

<section>
    <h2>Fontsource package</h2>
    <p>
      Add <code>@fontsource/jetbrains-mono</code> as a dependency and side-effect-import it from any component. The framework bundles the package's CSS (with woff2 inlined as data
      URIs), serves it from <code>/_mochi/import-css/*.css</code>, and links it from every page that transitively imports the component.
    </p>
    <p class="sample sample-mono">The quick brown fox jumps over the lazy dog. 1234567890</p>
  </section>

  <section>
    <h2>Standalone .woff2</h2>
    <p>
      Drop a <code>.woff2</code> next to your component and reference it from a tiny
      <code>@font-face</code> CSS file:
    </p>
    <pre><code
        >{`@font-face {
  font-family: 'Lobster';
  src: url('./lobster.woff2') format('woff2');
}`}</code
      ></pre>
    <p>
      Side-effect-import the CSS from the component (<code>import './lobster.css'</code>). Bun's CSS bundler inlines the <code>.woff2</code> as a base64 data URI in the bundled CSS,
      so the font ships in the same request as the stylesheet — no separate font fetch.
    </p>
    <p class="sample sample-display">The quick brown fox jumps over the lazy dog. 1234567890</p>
  </section>

  <section>
    <h2>Inside a hydratable island</h2>
    <p>
      The same import works in a component with <code>mochi:hydrate</code>. The framework strips the CSS import from both the SSR and the client bundle, then links the bundled
      stylesheet from the page <code>&lt;head&gt;</code>. The badge flips to <em>hydrated</em> in the browser; the styled text uses the bundled font on first paint with no JS-injected
      styles.
    </p>
    <HydratedBox mochi:hydrate />
  </section>
Styles
<style>
  section {
    margin: 2rem 0;
  }
  h2 {
    margin-bottom: 0.5rem;
    font-family: var(--font-serif);
  }
  .sample {
    margin-top: 1rem;
    padding: 1.25rem;
    background: var(--surface-muted);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    font-size: 1.5rem;
  }
  .sample-mono {
    font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  }
  .sample-display {
    font-family: 'Lobster', cursive;
    font-size: 2rem;
  }
  pre {
    margin: 0.75rem 0;
  }
</style>