useSubscription()
The useSubscription hook can be used to subscribe to a subscription procedure on the server.
Signature
Options
tip
- If you need to set any options but don't want to pass any input, you can pass
undefinedinstead. - If you pass
skipTokenfrom@tanstack/react-query, the subscription will be paused. - Have a look at our SSE example for a complete example of how to use subscriptions
tsxinterfaceUseTRPCSubscriptionOptions <TOutput ,TError > {/*** Called when the subscription is started.*/onStarted ?: () => void;/*** Called when new data is received from the subscription.*/onData ?: (data :TOutput ) => void;/*** Called when an **unrecoverable error** occurs and the subscription is stopped.*/onError ?: (error :TError ) => void;/*** Called when the subscription is completed on the server.* The state will transition to `'idle'` with `data: undefined`.*/onComplete ?: () => void;/*** @deprecated Use a `skipToken` from `@tanstack/react-query` instead.* This will be removed in v12.*/enabled ?: boolean;}
tsxinterfaceUseTRPCSubscriptionOptions <TOutput ,TError > {/*** Called when the subscription is started.*/onStarted ?: () => void;/*** Called when new data is received from the subscription.*/onData ?: (data :TOutput ) => void;/*** Called when an **unrecoverable error** occurs and the subscription is stopped.*/onError ?: (error :TError ) => void;/*** Called when the subscription is completed on the server.* The state will transition to `'idle'` with `data: undefined`.*/onComplete ?: () => void;/*** @deprecated Use a `skipToken` from `@tanstack/react-query` instead.* This will be removed in v12.*/enabled ?: boolean;}
Return type
The return type is a discriminated union on status:
tstypeTRPCSubscriptionResult <TOutput ,TError > =|TRPCSubscriptionIdleResult <TOutput >|TRPCSubscriptionConnectingResult <TOutput ,TError >|TRPCSubscriptionPendingResult <TOutput >|TRPCSubscriptionErrorResult <TOutput ,TError >;interfaceTRPCSubscriptionIdleResult <TOutput > {/** Subscription is disabled or has ended */status : 'idle';data : undefined;error : null;reset : () => void;}interfaceTRPCSubscriptionConnectingResult <TOutput ,TError > {/** Trying to establish a connection (may have a previous error from a reconnection attempt) */status : 'connecting';data :TOutput | undefined;error :TError | null;reset : () => void;}interfaceTRPCSubscriptionPendingResult <TOutput > {/** Connected to the server, receiving data */status : 'pending';data :TOutput | undefined;error : null;reset : () => void;}interfaceTRPCSubscriptionErrorResult <TOutput ,TError > {/** An unrecoverable error occurred and the subscription is stopped */status : 'error';data :TOutput | undefined;error :TError ;reset : () => void;}
tstypeTRPCSubscriptionResult <TOutput ,TError > =|TRPCSubscriptionIdleResult <TOutput >|TRPCSubscriptionConnectingResult <TOutput ,TError >|TRPCSubscriptionPendingResult <TOutput >|TRPCSubscriptionErrorResult <TOutput ,TError >;interfaceTRPCSubscriptionIdleResult <TOutput > {/** Subscription is disabled or has ended */status : 'idle';data : undefined;error : null;reset : () => void;}interfaceTRPCSubscriptionConnectingResult <TOutput ,TError > {/** Trying to establish a connection (may have a previous error from a reconnection attempt) */status : 'connecting';data :TOutput | undefined;error :TError | null;reset : () => void;}interfaceTRPCSubscriptionPendingResult <TOutput > {/** Connected to the server, receiving data */status : 'pending';data :TOutput | undefined;error : null;reset : () => void;}interfaceTRPCSubscriptionErrorResult <TOutput ,TError > {/** An unrecoverable error occurred and the subscription is stopped */status : 'error';data :TOutput | undefined;error :TError ;reset : () => void;}
Example Procedure
server/routers/_app.tstsximportEventEmitter , {on } from 'events';import {initTRPC } from '@trpc/server';export constt =initTRPC .create ();typePost = {id : string;title : string };constee = newEventEmitter ();export constappRouter =t .router ({onPostAdd :t .procedure .subscription (async function* (opts ) {for await (const [data ] ofon (ee , 'add', {signal :opts .signal ,})) {constpost =data asPost ;yieldpost ;}}),});export typeAppRouter = typeofappRouter ;
server/routers/_app.tstsximportEventEmitter , {on } from 'events';import {initTRPC } from '@trpc/server';export constt =initTRPC .create ();typePost = {id : string;title : string };constee = newEventEmitter ();export constappRouter =t .router ({onPostAdd :t .procedure .subscription (async function* (opts ) {for await (const [data ] ofon (ee , 'add', {signal :opts .signal ,})) {constpost =data asPost ;yieldpost ;}}),});export typeAppRouter = typeofappRouter ;
Example React Component
components/PostFeed.tsxtsximport {trpc } from '../utils/trpc';typePost = {id : string;title : string };export functionPostFeed () {const [posts ,setPosts ] =React .useState <Post []>([]);constsubscription =trpc .onPostAdd .useSubscription (undefined , {onData : (post ) => {setPosts ((prev ) => [...prev ,post ]);},});return (<div ><h1 >Live Feed</h1 >{subscription .status === 'connecting' && <p >Connecting...</p >}{subscription .status === 'error' && (<div ><p >Error: {subscription .error .message }</p ><button onClick ={() =>subscription .reset ()}>Reconnect</button ></div >)}<ul >{posts .map ((post ) => (<li key ={post .id }>{post .title }</li >))}</ul ></div >);}
components/PostFeed.tsxtsximport {trpc } from '../utils/trpc';typePost = {id : string;title : string };export functionPostFeed () {const [posts ,setPosts ] =React .useState <Post []>([]);constsubscription =trpc .onPostAdd .useSubscription (undefined , {onData : (post ) => {setPosts ((prev ) => [...prev ,post ]);},});return (<div ><h1 >Live Feed</h1 >{subscription .status === 'connecting' && <p >Connecting...</p >}{subscription .status === 'error' && (<div ><p >Error: {subscription .error .message }</p ><button onClick ={() =>subscription .reset ()}>Reconnect</button ></div >)}<ul >{posts .map ((post ) => (<li key ={post .id }>{post .title }</li >))}</ul ></div >);}