Skip to content

Astro

Tegaki has an Astro component that renders full HTML at build time and hydrates on the client. It needs no JavaScript framework, and the text is visible before hydration.

npm install tegaki

Then add the Tegaki integration to your Astro config:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import tegaki from 'tegaki/astro/integration';

export default defineConfig({
  integrations: [tegaki()],
});

The component renders on the server and hands the font bundle to the browser, so the bundle’s font URLs have to work there. Vite turns them into public asset URLs for client code but leaves server code as it stands, where they point at files on the build machine. The integration makes Vite resolve them on the server too. Without it, the build warns and the page loads the handwriting in a fallback font’s widths.

---
import TegakiRenderer from 'tegaki/astro';
import bundle from 'tegaki/fonts/caveat';
---

<TegakiRenderer
  font={bundle}
  text="Hello World"
  time={{ mode: 'uncontrolled', speed: 1, loop: true }}
  style="font-size: 48px"
/>
Hello World

When using the same font across multiple components on a page, you can register the bundle once and reference it by name. This avoids duplicating the font data in the HTML output.

Use the bundle prop on a dedicated instance. It serializes the font data into a <script type="application/json"> tag and registers it on the client. Once registered, reference the font by its family name (the fullFamily field of the bundle, e.g. "Caveat"):

---
import TegakiRenderer from 'tegaki/astro';
import bundle from 'tegaki/fonts/caveat';
---

<!-- Register the bundle once (renders nothing visible) -->
<TegakiRenderer font={bundle} bundle />

<!-- Use the font by name -->
<TegakiRenderer font="Caveat" text="First line" />
<TegakiRenderer font="Caveat" text="Second line" />

Add loadFont to inject an @font-face rule so the browser starts downloading the font file immediately:

<TegakiRenderer font={bundle} bundle loadFont />
---
import TegakiRenderer from 'tegaki/astro';
import bundle from 'tegaki/fonts/caveat';
---

<TegakiRenderer
  font={bundle}
  text="Fancy effects"
  time={{ mode: 'uncontrolled', speed: 1, loop: true }}
  effects={{
    glow: { radius: 8, color: '#00ccff' },
    pressureWidth: true,
    strokeGradient: { colors: 'rainbow' },
  }}
  style="font-size: 48px"
/>
Fancy effects
  1. Build time: the Astro component calls TegakiEngine.renderElements() to produce the full HTML (canvas fallback text, overlay, sentinel). Text is visible in the initial HTML even without JavaScript.
  2. Bundle serialization: because font glyph data lives in server memory during SSR, it must be embedded into the HTML to reach the browser. When font={bundle} is passed as an object, the component serializes the bundle into a <script type="application/json"> tag. If the same font is used across multiple renderers on the same page, use the bundle prop once to serialize it and reference it by name afterward, so the bundle data isn’t duplicated in the HTML output.
  3. Client hydration: a module script reads any serialized bundle tags and registers them, then finds all [data-tegaki-options] elements, creates a TegakiEngine with adopt: true (reusing existing DOM), and starts the animation.
  4. Dynamic elements: a MutationObserver watches for elements added after initial load (e.g., from View Transitions or client-side routing) and hydrates them.

The Astro component accepts all TegakiEngineOptions plus:

Prop Type Description
bundle boolean Register the font bundle for client-side lookup by name
loadFont boolean Inject @font-face CSS for early font loading
class string CSS class on the container