Skip to content

Phaze + Astro

Phaze ships an Astro integration that registers it as a renderer. @madenowhere/phaze-render-to-string is wired — every Astro client directive works: client:only, client:load, client:idle, client:visible, client:media, plus server-rendered (no-directive) islands and prerender:true static pages.

Two islands below — same component, different directives:

client:only — mounts in the browser, no SSR HTML

client:load — server-rendered HTML, then client mounts on page load

client:load: 10
Terminal window
pnpm add @madenowhere/phaze-astro @madenowhere/phaze astro
astro.config.mjs
import { defineConfig } from 'astro/config'
import phaze from '@madenowhere/phaze-astro'
export default defineConfig({
integrations: [phaze()],
})

The integration:

  • registers Phaze as a renderer Astro can dispatch to
  • configures Vite’s esbuild to use jsxImportSource: '@madenowhere/phaze'
  • provides a client mount entry that calls render(component, element) for each island

Match the JSX import source so editor tooling agrees with the build:

{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@madenowhere/phaze"
}
}
src/components/Counter.tsx
import { signal } from '@madenowhere/phaze'
export default function Counter({ start = 0 }: { start?: number }) {
const count = signal(start)
return (
<button onClick={() => count.set(count.current() + 1)}>
{count}
</button>
)
}
src/pages/index.astro
---
import Counter from '../components/Counter.tsx'
---
<html><body>
<h1>Hello</h1>
<Counter client:only="phaze" start={5} />
</body></html>
  • All Astro client directivesclient:only, client:load, client:idle, client:visible, client:media, plus directive-less server-only islands and prerender:true static pages.
  • SSR + hydration@madenowhere/phaze-astro/server delegates to @madenowhere/phaze-render-to-string, which runs the same JSX runtime against a linkedom virtual DOM and serializes the result. The browser then mounts via hydrate (when SSR’d HTML is present) or render (for client:only); the choice is made by phaze-astro/client based on whether the host element has children.
  • View transitions — Starlight/Astro’s <ClientRouter /> is supported; phaze-astro/client registers an astro:after-swap listener that disposes phaze scopes whose host elements drop out of the document during a swap.
  • Props pass through serializable values normally; non-serializable values (functions, DOM nodes) need to live inside the island.
  • Multiple islands per page; each gets its own dispose-bounded scope.