Choose your integration
Choose a complete setup for your app. Each recipe includes its dependencies, provider where needed, component and mounting instructions.
Use one subscription path for each view: component state or your existing data library.
Match your backend
Section titled “Match your backend”All seven sources below work with the React, Vue, Svelte and Solid bindings
and the vanilla spinetab.subscribe() API. TanStack Query and SWR also accept
these sources; their map option adapts events to your data shape. TanStack
Query also provides reduce for accumulating updates.
| Backend | Source | Library integration |
|---|---|---|
| HTTP JSON | polling() |
TanStack Query or SWR |
| Server-sent events | sse() |
TanStack Query or SWR |
| Raw WebSocket | websocket() |
TanStack Query or SWR |
| Fetch stream | stream() |
TanStack Query or SWR |
| GraphQL over WebSocket | graphqlWs().subscription() |
Apollo, TanStack Query or SWR |
| GraphQL over SSE | graphqlSse().subscription() |
Apollo, TanStack Query or SWR |
| Socket.IO | socketIo().subscription() |
TanStack Query or SWR |
Apollo accepts the GraphQL endpoint directly
and builds operations from your documents. With TanStack Query, SWR or a UI
binding, build the .subscription() request yourself. GraphQL results include
data and errors; Socket.IO events are argument arrays. Neither is silently
converted into an application value.
Two integrations provide their own transport path:
- tRPC uses
spinetabWsLinkorspinetabSseLinkwith a tRPC server. Keep calling the typed tRPC client; do not wrap it inwebsocket()orsse(). - AI SDK uses
SpinetabChatTransportwith the AI SDK UI message stream protocol. It handles chat start/resume requests, not arbitrary polling or WebSocket feeds.
Choose where the data lives
Section titled “Choose where the data lives”| Your data layer | Frontend fit | Start here |
|---|---|---|
| Component state | React, Vue, Svelte, Solid; vanilla callbacks | Using subscriptions |
| TanStack Query | Any of its React, Vue, Svelte or Solid clients backed by the compatible Query core | Bind a cache and clean up by framework |
| SWR | React, including Next.js and React Router | SWR subscription hook |
| Apollo | Apollo Client 4’s link API; use a UI library compatible with that version | GraphQL link and React example |
| tRPC | Framework-independent typed client; keep your existing UI integration | Subscription links and cleanup |
| AI SDK | A chat UI accepting the installed AI SDK’s ChatTransport interface |
Chat transport and follow lifecycle |
SWR’s hook cannot be used in Vue, Svelte or Solid. Apollo and AI SDK transport compatibility does not imply that every third-party UI wrapper supports the required peer version; check that wrapper before adopting it. For GraphQL without an Apollo wrapper, use the direct source with a Spinetab UI binding.
Keep your framework’s client boundary
Section titled “Keep your framework’s client boundary”The backend choice does not change the build plugin or component lifecycle. The library choice may add its own provider or cache; Spinetab does not replace it.
| Setup | Where the subscription belongs |
|---|---|
| Vite | In the component lifecycle for React, Vue, Svelte or Solid |
| Next.js | Below a "use client" boundary; keep library providers in that client tree |
| Nuxt | In a Vue component; mount imperative bindings in the browser, not a server plugin |
| SvelteKit | In a component store or onMount; not a server load function |
| React Router | In the route component; not a server loader or action |
| Astro | Inside a hydrated island; keep library providers and consumers in the same island |
| Other bundlers / vanilla | Start when the browser view mounts and dispose when it is removed |
With server rendering, keep request-specific data and library caches scoped to that request. A shared browser connection does not share a cache between tabs or make a server module singleton safe for user data.
Adapt protocol results
Section titled “Adapt protocol results”These are the two extra data shapes to handle when replacing the plain JSON
sources in a framework guide. Declare anonymous: true or a credentials
callback on your client, and install the protocol’s peer libraries.
import { graphqlWs } from "spinetab/graphql-ws";import { QueueChangedDocument } from "./generated/graphql";
export const queueSource = graphqlWs("/graphql").subscription({ query: QueueChangedDocument,});QueueChangedDocument is your generated typed document for
subscription { queue { open } }. A plain query string also works, but its result
fields are untyped. Install graphql and graphql-ws. For GraphQL over SSE, install graphql-sse
and use graphqlSse("/graphql/stream") instead. The request and result shape
stay the same.
The binding’s data is a GraphQL result: read data?.data?.queue.open and handle
data?.errors as well as the binding’s transport error. In TanStack Query or
SWR, a map callback can select result.data?.queue; decide explicitly how your
app handles partial results and GraphQL errors before caching them.
import { socketIo } from "spinetab/socket-io";
export const queueSource = socketIo("/", { sharing: "shared" }).subscription< [{ open: number }]>({ event: "queue" });Install socket.io-client. This expects the server to emit
socket.emit("queue", { open: 12 }). Read data?.[0].open, or use
map: ([queue]) => queue in the binding, TanStack Query or SWR.
Only choose sharing: "shared" if merging tabs onto one socket suits your server;
see rooms and sharing.
Choose recovery from the data, not the transport
Section titled “Choose recovery from the data, not the transport”- Complete current value on every event:
reconcile: "latest"restores continuity when the next complete value arrives. - Incremental changes: refresh a snapshot and merge it with live events, or use server replay with a policy for delivery overflow.
- One-off work: do not make a request repeatable just to enable sharing. Use the operation’s explicit start/resume contract.
See recovery policies for implementations. The same choice applies in every framework; a reconnected transport alone does not prove that your displayed data is complete.