rembrembdocs

The useSubscription hook can be used to subscribe to a subscription procedure on the server.

Signature

Options

tip

tsx

interface UseTRPCSubscriptionOptions<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:

ts

type TRPCSubscriptionResult<TOutput, TError> =

| TRPCSubscriptionIdleResult<TOutput>

| TRPCSubscriptionConnectingResult<TOutput, TError>

| TRPCSubscriptionPendingResult<TOutput>

| TRPCSubscriptionErrorResult<TOutput, TError>;

interface TRPCSubscriptionIdleResult<TOutput> {

/** Subscription is disabled or has ended */

status: 'idle';

data: undefined;

error: null;

reset: () => void;

}

interface TRPCSubscriptionConnectingResult<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;

}

interface TRPCSubscriptionPendingResult<TOutput> {

/** Connected to the server, receiving data */

status: 'pending';

data: TOutput | undefined;

error: null;

reset: () => void;

}

interface TRPCSubscriptionErrorResult<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.ts

tsx

import EventEmitter, { on } from 'events';

import { initTRPC } from '@trpc/server';

export const t = initTRPC.create();

type Post = { id: string; title: string };

const ee = new EventEmitter();

export const appRouter = t.router({

onPostAdd: t.procedure.subscription(async function* (opts) {

`for await (const [data] of on(ee, 'add', {`

  `signal: opts.signal,`

`})) {`

  `const post = data as Post;`

  `yield post;`

`}`

}),

});

export type AppRouter = typeof appRouter;

Example React Component

components/PostFeed.tsx

tsx

import { trpc } from '../utils/trpc';

type Post = { id: string; title: string };

export function PostFeed() {

const [posts, setPosts] = React.useState<Post[]>([]);

const subscription = 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>`

);

}