Skip to content

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.

Use a complete recipe for your renderer and data library, or continue below for the basic direct subscription.

Open recipe

Terminal window
pnpm add spinetab

Keep your existing integrations and add spinetab(). This example assumes @astrojs/react is installed and configured for React components:

astro.config.mjs
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.

src/live.ts
import { createSpinetab } from "spinetab";
import { bindClient } from "spinetab/react";
export const spinetab = createSpinetab();
export const { useLive } = bindClient(spinetab);

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.

queue-source.ts
import { polling } from "spinetab/polling";
export const queueSource = polling<{ open: number }>("/api/queue");

Polling options

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.

src/components/Queue.tsx
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>;
}
src/pages/queue.astro
---
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.

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.

See Astro configuration for plugin options.