Astro
Use Nizivo in Astro 7 with @astrojs/react.
Everything on this page is checked by the Astro fixture (fixtures/astro, Astro 7.3.8 with
@astrojs/react 7.0.1, static output, astro build and astro preview) on the packed packages,
except where a note says otherwise.
Install
pnpm add @nizivo/react@next @nizivo/tokens@nextAdd React to Astro with @astrojs/react. @nizivo/react needs react and react-dom ^19.2.0.
Import the CSS once
In your base layout, import the tokens, the accessibility floor and one stylesheet per component you
use. Keep the .css extension. Astro turns the imports into one render-blocking <link> on every
page, server-only pages included, and keeps every light-dark() value with no extra setting.
---
import '@nizivo/tokens/tokens.css';
import '@nizivo/react/css/base.css';
import '@nizivo/react/css/card.css';
import { themeScript } from '@nizivo/react/theme-script';
---
<html lang="en" data-nz-theme="system">
<head>
<script is:inline set:html={themeScript} />
</head>
<body><slot /></body>
</html>Theme
data-nz-theme="system" follows the operating system with no script and no flash; light or dark
forces one theme. themeScript, inline in <head>, applies a stored choice before first paint: it
reads localStorage key nz-theme (light, dark or system) and sets data-nz-theme on <html>.
It also sets data-nz-platform so Kbd shows ⌘ or Ctrl from the first frame. Your theme switch saves
the choice with localStorage.setItem('nz-theme', theme) and sets the attribute itself; see
Next.js for the details and the CSP hash. The fixture checks every
frame from first paint for the OS preference, a stored choice and a static attribute, and catches a
theme set late as a flash.
Instead of the three stylesheets above, you can import @nizivo/react/styles.css alone: the tokens,
base.css and every component. Import either it or the separate files, never both. The fixture checks
the separate files.
Islands
- A component rendered without a
client:*directive is static HTML and ships no JavaScript: the fixture's server-only pages load no script at all, barrel imports included. - Children passed from an
.astrofile arrive as static HTML, outside their Root's context. Compose a compound component (aSelect.Rootwith its parts) inside one.tsxisland. With the parts as.astrochildren the build fails with React Aria's "ListBoxItem cannot be rendered outside a collection" (seen once on a throwaway page; the fixture does not keep a failing build). - Each island gets its own
useIdprefix, so two islands on one page never repeat an id. client:loadandclient:visiblehydrate with no console message.- Context does not cross islands: an island that needs a non-default locale wraps its own
NizivoConfig. This follows from how Astro hydrates islands; the fixture does not exercise it. - Props to a
client:*island must be serialisable, so no functions. This is Astro's rule; the fixture does not exercise it.
Running Astro under a coding agent
Astro 7 detects coding agents and then runs astro dev and astro preview in the background with a
lock file. Pass --ignore-lock to keep them in the foreground. astro build behaves the same either
way. Set ASTRO_TELEMETRY_DISABLED=1 to stop Astro's telemetry.
Not covered by the fixture yet
output: 'server' (it needs an adapter), astro dev, client:idle and client:only are not checked
yet. The fixture runs in Chromium.