Astro
Add live updates to a hydrated Astro island. Choose your renderer and data library below for a complete recipe. The walkthrough on this page uses React.
The examples display a queue value such as { "open": 12 }. Choose the source
that matches your backend in step 3.
Replace the URL and type with your API. A static Astro build does not create a
live API endpoint: use an existing backend, with CORS configured if it is on a
different origin.
Choose your stack
Section titled “Choose your stack”Use a complete recipe for your renderer and data library, or continue below for the basic direct subscription.
1. Add the integrations
Section titled “1. Add the integrations”pnpm add spinetabKeep your existing integrations and add spinetab(). This example assumes
@astrojs/react
is installed and configured for React components:
import react from "@astrojs/react";import { defineConfig } from "astro/config";import { spinetab } from "spinetab/astro";
export default defineConfig({ integrations: [react(), spinetab()],});The Spinetab integration adds the worker build to Astro’s client build. Add it
manually; astro add spinetab is not supported. Restart the dev server after
changing the config.
2. Create the client
Section titled “2. Create the client”import { createSpinetab } from "spinetab";import { bindClient } from "spinetab/react";
export const spinetab = createSpinetab();export const { useLive } = bindClient(spinetab);3. Choose your source
Section titled “3. Choose your source”Choose the format your server already serves. Save one of these as
queue-source.ts alongside live.ts. Each example receives the complete queue
value, { "open": 12 }, so the component below stays the same.
Read a JSON response from GET /api/queue every five seconds by default.
import { polling } from "spinetab/polling";
export const queueSource = polling<{ open: number }>("/api/queue");Your endpoint serves text/event-stream with JSON in each data: field.
import { sse } from "spinetab/sse";
export const queueSource = sse<{ open: number }>("/api/queue/events");Your WebSocket endpoint sends a JSON queue value in each text frame. This is a raw WebSocket feed; GraphQL and Socket.IO need their own adapters.
import { websocket } from "spinetab/websocket";
export const queueSource = websocket<{ open: number }>("/ws/queue", { decoder: "json",});Your endpoint streams one JSON value per line (NDJSON). Only declare it repeatable when opening the request again is safe.
import { stream } from "spinetab/stream";
export const queueSource = stream<{ open: number }>("/api/queue/stream", { repeatable: true,});These URLs are examples, not routes created by the plugin. Replace them with your API. For GraphQL, Socket.IO or a client library, see how sources and integrations fit together.
4. Add a live component
Section titled “4. Add a live component”import { queueSource } from "../queue-source";import { useLive } from "../live";
export function Queue() { const { data, error } = useLive(queueSource, { reconcile: "latest", });
if (error) return <p role="alert">Could not load the queue: {error.code}</p>; if (data === undefined) return <p role="status">Loading queue…</p>;
return <p>{data.open} open</p>;}5. Hydrate it on a page
Section titled “5. Hydrate it on a page”---import { Queue } from "../components/Queue";---
<html lang="en"> <head> <meta charset="UTF-8" /> <title>Queue</title> </head> <body> <h1>Queue</h1> <Queue client:load /> </body></html>client:load
hydrates the component on page load. Without a client:* directive, Astro renders
HTML only and the subscription never starts. Use client:visible if the feed
should start only when its island becomes visible.
Server rendering produces the loading state without opening a connection.
The React hook starts work when the island mounts and releases the subscription
when it unmounts. You do not need client:only for this component.
Check it across tabs
Section titled “Check it across tabs”Visit /queue in your running app.
Open the page in two tabs of the same browser profile. With SharedWorker available, matching subscriptions share the upstream feed or polling schedule. Each component keeps its own value. For polling, a new subscriber can trigger a fresh read; check ongoing requests rather than expecting exactly one initial request.
Polling reads every five seconds by default and pauses when no consumer is eligible, such as when all subscribing tabs are hidden. It reads again when a tab returns. If worker sharing is unavailable, each tab runs its own subscriptions; see execution modes.
reconcile: "latest" fits these full-state feeds. After a delivery gap, the next
value restores the displayed state.
Use a refresh policy for feeds of incremental changes.
Next steps
Section titled “Next steps”- Use GraphQL, Socket.IO or a client library with this setup.
- Add authentication and user scopes for private data. Keep request-specific credentials out of server module singletons.
- Read continuity and recovery before using an event stream: reconnecting alone cannot recover missed events.
- Check compatible versions and restrictions for your stack.
See Astro configuration for plugin options.