useSubscription()
On this page
The useSubscription hook can be used to subscribe to a subscription procedure on the server.
Signature
Options
interface interface UseTRPCSubscriptionOptions<TOutput, TError>UseTRPCSubscriptionOptions<function (type parameter) TOutput in UseTRPCSubscriptionOptions<TOutput, TError>TOutput, function (type parameter) TError in UseTRPCSubscriptionOptions<TOutput, TError>TError> {
/**
* Called when the subscription is started.
*/
UseTRPCSubscriptionOptions<TOutput, TError>.onStarted?: (() => void) | undefinedonStarted?: () => void;
/**
* Called when new data is received from the subscription.
*/
UseTRPCSubscriptionOptions<TOutput, TError>.onData?: ((data: TOutput) => void) | undefinedonData?: (data: TOutputdata: function (type parameter) TOutput in UseTRPCSubscriptionOptions<TOutput, TError>TOutput) => void;
/**
* Called when an **unrecoverable error** occurs and the subscription is stopped.
*/
UseTRPCSubscriptionOptions<TOutput, TError>.onError?: ((error: TError) => void) | undefinedonError?: (error: TErrorerror: function (type parameter) TError in UseTRPCSubscriptionOptions<TOutput, TError>TError) => void;
/**
* Called when the subscription is completed on the server.
* The state will transition to `'idle'` with `data: undefined`.
*/
UseTRPCSubscriptionOptions<TOutput, TError>.onComplete?: (() => void) | undefinedonComplete?: () => void;
/**
* @deprecated Use a `skipToken` from `@tanstack/react-query` instead.
* This will be removed in v12.
*/
UseTRPCSubscriptionOptions<TOutput, TError>.enabled?: boolean | undefinedenabled?: boolean;
}Return type
The return type is a discriminated union on status:
type type TRPCSubscriptionResult<TOutput, TError> = TRPCSubscriptionIdleResult<TOutput> | TRPCSubscriptionConnectingResult<TOutput, TError> | TRPCSubscriptionPendingResult<TOutput> | TRPCSubscriptionErrorResult<TOutput, TError>TRPCSubscriptionResult<function (type parameter) TOutput in type TRPCSubscriptionResult<TOutput, TError>TOutput, function (type parameter) TError in type TRPCSubscriptionResult<TOutput, TError>TError> =
| interface TRPCSubscriptionIdleResult<TOutput>TRPCSubscriptionIdleResult<function (type parameter) TOutput in type TRPCSubscriptionResult<TOutput, TError>TOutput>
| interface TRPCSubscriptionConnectingResult<TOutput, TError>TRPCSubscriptionConnectingResult<function (type parameter) TOutput in type TRPCSubscriptionResult<TOutput, TError>TOutput, function (type parameter) TError in type TRPCSubscriptionResult<TOutput, TError>TError>
| interface TRPCSubscriptionPendingResult<TOutput>TRPCSubscriptionPendingResult<function (type parameter) TOutput in type TRPCSubscriptionResult<TOutput, TError>TOutput>
| interface TRPCSubscriptionErrorResult<TOutput, TError>TRPCSubscriptionErrorResult<function (type parameter) TOutput in type TRPCSubscriptionResult<TOutput, TError>TOutput, function (type parameter) TError in type TRPCSubscriptionResult<TOutput, TError>TError>;
interface interface TRPCSubscriptionIdleResult<TOutput>TRPCSubscriptionIdleResult<function (type parameter) TOutput in TRPCSubscriptionIdleResult<TOutput>TOutput> {
/** Subscription is disabled or has ended */
TRPCSubscriptionIdleResult<TOutput>.status: "idle"status: 'idle';
TRPCSubscriptionIdleResult<TOutput>.data: undefineddata: undefined;
TRPCSubscriptionIdleResult<TOutput>.error: nullerror: null;
TRPCSubscriptionIdleResult<TOutput>.reset: () => voidreset: () => void;
}
interface interface TRPCSubscriptionConnectingResult<TOutput, TError>TRPCSubscriptionConnectingResult<function (type parameter) TOutput in TRPCSubscriptionConnectingResult<TOutput, TError>TOutput, function (type parameter) TError in TRPCSubscriptionConnectingResult<TOutput, TError>TError> {
/** Trying to establish a connection (may have a previous error from a reconnection attempt) */
TRPCSubscriptionConnectingResult<TOutput, TError>.status: "connecting"status: 'connecting';
TRPCSubscriptionConnectingResult<TOutput, TError>.data: TOutput | undefineddata: function (type parameter) TOutput in TRPCSubscriptionConnectingResult<TOutput, TError>TOutput | undefined;
TRPCSubscriptionConnectingResult<TOutput, TError>.error: TError | nullerror: function (type parameter) TError in TRPCSubscriptionConnectingResult<TOutput, TError>TError | null;
TRPCSubscriptionConnectingResult<TOutput, TError>.reset: () => voidreset: () => void;
}
interface interface TRPCSubscriptionPendingResult<TOutput>TRPCSubscriptionPendingResult<function (type parameter) TOutput in TRPCSubscriptionPendingResult<TOutput>TOutput> {
/** Connected to the server, receiving data */
TRPCSubscriptionPendingResult<TOutput>.status: "pending"status: 'pending';
TRPCSubscriptionPendingResult<TOutput>.data: TOutput | undefineddata: function (type parameter) TOutput in TRPCSubscriptionPendingResult<TOutput>TOutput | undefined;
TRPCSubscriptionPendingResult<TOutput>.error: nullerror: null;
TRPCSubscriptionPendingResult<TOutput>.reset: () => voidreset: () => void;
}
interface interface TRPCSubscriptionErrorResult<TOutput, TError>TRPCSubscriptionErrorResult<function (type parameter) TOutput in TRPCSubscriptionErrorResult<TOutput, TError>TOutput, function (type parameter) TError in TRPCSubscriptionErrorResult<TOutput, TError>TError> {
/** An unrecoverable error occurred and the subscription is stopped */
TRPCSubscriptionErrorResult<TOutput, TError>.status: "error"status: 'error';
TRPCSubscriptionErrorResult<TOutput, TError>.data: TOutput | undefineddata: function (type parameter) TOutput in TRPCSubscriptionErrorResult<TOutput, TError>TOutput | undefined;
TRPCSubscriptionErrorResult<TOutput, TError>.error: TErrorerror: function (type parameter) TError in TRPCSubscriptionErrorResult<TOutput, TError>TError;
TRPCSubscriptionErrorResult<TOutput, TError>.reset: () => voidreset: () => void;
}Example Procedure
server/routers/_app.ts
import class EventEmitter<T extends EventMap<T> = any>
interface EventEmitter<T extends EventMap<T> = any>
EventEmitter, { function on(emitter: EventEmitter, eventName: string | symbol, options?: EventEmitter.OnOptions): NodeJS.AsyncIterator<any[]> (+1 overload)on } from 'events';
import { const initTRPC: TRPCBuilder<object, object>initTRPC } from '@trpc/server';
export const const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t = const initTRPC: TRPCBuilder<object, object>initTRPC.TRPCBuilder<object, object>.create<RuntimeConfigOptions<object, object>>(opts?: RuntimeConfigOptions<object, object> | undefined): TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
create();
type type Post = {
id: string;
title: string;
}
Post = { id: stringid: string; title: stringtitle: string };
const const ee: EventEmitter<any>ee = new new EventEmitter<any>(options?: EventEmitter.EventEmitterOptions): EventEmitter<any>EventEmitter();
export const const appRouter: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
appRouter = const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, { ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder
<{
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>(_: {
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>;
}) => BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router({
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>
onPostAdd: const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, { ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>procedure.ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.subscription<AsyncGenerator<Post, void, any>>(resolver: ProcedureResolver<object, object, object, UnsetMarker, UnsetMarker, AsyncGenerator<Post, void, any>>): SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}> (+1 overload)
subscription(async function* (opts: ProcedureResolverOptions<object, object, object, UnsetMarker>opts) {
for await (const [const data: anydata] of function on(emitter: EventEmitter, eventName: string | symbol, options?: EventEmitter.OnOptions): NodeJS.AsyncIterator<any[]> (+1 overload)on(const ee: EventEmitter<any>ee, 'add', {
EventEmitter<T extends EventMap<T> = any>.Abortable.signal?: AbortSignal | undefinedsignal: opts: ProcedureResolverOptions<object, object, object, UnsetMarker>opts.ProcedureResolverOptions<object, object, object, UnsetMarker>.signal: AbortSignal | undefinedsignal,
})) {
const const post: Postpost = const data: anydata as type Post = {
id: string;
title: string;
}
Post;
yield const post: Postpost;
}
}),
});
export type type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>> & DecorateCreateRouterOptions<{
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>
AppRouter = typeof const appRouter: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
appRouter;Example React Component
components/PostFeed.tsx
import { const trpc: CreateTRPCReactBase<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>, unknown> & DecorateRouterRecord<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
trpc } from '../utils/trpc';
type type Post = {
id: string;
title: string;
}
Post = { id: stringid: string; title: stringtitle: string };
export function function PostFeed(): React.JSX.ElementPostFeed() {
const [const posts: Post[]posts, const setPosts: React.Dispatch<React.SetStateAction<Post[]>>setPosts] = React.function React.useState<Post[]>(initialState: Post[] | (() => Post[])): [Post[], React.Dispatch<React.SetStateAction<Post[]>>] (+1 overload)useState<type Post = {
id: string;
title: string;
}
Post[]>([]);
const const subscription: TRPCSubscriptionResult<Post, TRPCClientErrorLike<{
input: void;
output: AsyncIterable<Post, never, any>;
transformer: false;
errorShape: DefaultErrorShape;
}>>
subscription = const trpc: CreateTRPCReactBase<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>, unknown> & DecorateRouterRecord<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
onPostAdd: SubscriptionProcedure<{
input: void;
output: AsyncIterable<Post, void, any>;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
trpc.onPostAdd: {
useSubscription: ProcedureUseSubscription<{
input: void;
output: AsyncIterable<Post, never, any>;
transformer: false;
errorShape: DefaultErrorShape;
}>;
}
onPostAdd.useSubscription: ProcedureUseSubscription
(input: void, opts?: UseTRPCSubscriptionOptions<Post, TRPCClientErrorLike<{
input: void;
output: AsyncIterable<Post, never, any>;
transformer: false;
errorShape: DefaultErrorShape;
}>> | undefined) => TRPCSubscriptionResult<Post, TRPCClientErrorLike<{
input: void;
output: AsyncIterable<Post, never, any>;
transformer: false;
errorShape: DefaultErrorShape;
}>> (+1 overload)
useSubscription(var undefinedundefined, {
UseTRPCSubscriptionOptions<Post, TRPCClientErrorLike<{ input: void; output: AsyncIterable<Post, never, any>; transformer: false; errorShape: DefaultErrorShape; }>>.onData?: ((data: Post) => void) | undefinedonData: (post: Postpost) => {
const setPosts: (value: React.SetStateAction<Post[]>) => voidsetPosts((prev: Post[]prev) => [...prev: Post[]prev, post: Postpost]);
},
});
return (
<React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<React.JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1>Live Feed</React.JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1>
{const subscription: TRPCSubscriptionResult<Post, TRPCClientErrorLike<{
input: void;
output: AsyncIterable<Post, never, any>;
transformer: false;
errorShape: DefaultErrorShape;
}>>
subscription.status: "idle" | "connecting" | "error" | "pending"status === 'connecting' && <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Connecting...</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>}
{const subscription: TRPCSubscriptionResult<Post, TRPCClientErrorLike<{
input: void;
output: AsyncIterable<Post, never, any>;
transformer: false;
errorShape: DefaultErrorShape;
}>>
subscription.status: "idle" | "connecting" | "error" | "pending"status === 'error' && (
<React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Error: {const subscription: TRPCSubscriptionErrorResult<Post, TRPCClientErrorLike<{
input: void;
output: AsyncIterable<Post, never, any>;
transformer: false;
errorShape: DefaultErrorShape;
}>>
subscription.TRPCSubscriptionErrorResult<Post, TRPCClientErrorLike<{ input: void; output: AsyncIterable<Post, never, any>; transformer: false; errorShape: DefaultErrorShape; }>>.error: TRPCClientErrorLike<{
input: void;
output: AsyncIterable<Post, never, any>;
transformer: false;
errorShape: DefaultErrorShape;
}>
error.TRPCClientErrorBase<DefaultErrorShape>.message: stringmessage}</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>
<React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={() => const subscription: TRPCSubscriptionErrorResult<Post, TRPCClientErrorLike<{
input: void;
output: AsyncIterable<Post, never, any>;
transformer: false;
errorShape: DefaultErrorShape;
}>>
subscription.TRPCSubscriptionBaseResult<Post, TRPCClientErrorLike<{ input: void; output: AsyncIterable<Post, never, any>; transformer: false; errorShape: DefaultErrorShape; }>>.reset: () => voidreset()}>Reconnect</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
)}
<React.JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
{const posts: Post[]posts.Array<Post>.map<React.JSX.Element>(callbackfn: (value: Post, index: number, array: Post[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]map((post: Postpost) => (
<React.JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={post: Postpost.id: stringid}>{post: Postpost.title: stringtitle}</React.JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</React.JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}