# Subscriptions in TanStack Query

> subscriptionOptions() for each framework's subscription primitive, shared between components, and liveOptions() and streamedOptions() in the query cache.

A subscription procedure has three helpers. `subscriptionOptions()` feeds your framework's subscription primitive (`useSubscription`, `createSubscription`, `injectSubscription` or `createSubscriptionController`), outside the query cache. `liveOptions()` and `streamedOptions()` put the subscription in the query cache instead, for `useQuery`.

## Outside the query cache

Each framework's primitive gives `status` (`idle`, `connecting`, `active`, `error`), the latest event as `data`, `error` and `reset()`:

```tsx
function NewPosts(props: { channel: string }) {
  const sub = useSubscription(
    trpc.post.onAdd.subscriptionOptions({
      input: { channel: props.channel },
      onData: (event) => toast(event.data.title),
    }),
  );
  if (sub.status === 'error') return <button onClick={sub.reset}>Retry</button>;
  return <p>Latest: {sub.data?.data.title}</p>;
}
```

- `idle` means disabled (`enabled: false` or `input: skipToken`), not started yet, or ended by the server; `connecting` means started with no event yet; `error` stays until `reset()` starts it again.
- `onData`, `onError` and `onComplete` always run with the latest options, so changing them doesn't restart the subscription. A new input (a new key) or `enabled` does.
- Components with the same client and key share one subscription. A late one starts from the current state, each component's `onData` runs for every later event, `reset()` restarts it for all of them, and it closes when the last one unmounts. Pass `subscriptionCache: undefined` to give an observer its own.
- Reconnects resume from the last `tracked()` id, as with any client ([Subscriptions](https://v12.trpc.io/v12/docs/subscriptions/overview.md)).

`SubscriptionObserver` from `@trpc12/tanstack-query` is what the primitives are built on, for a framework without a package.

## In the query cache

`liveOptions()` keeps the latest event as the query's data; `streamedOptions()` keeps every event, built on TanStack's `streamedQuery`, capped by `maxChunks`. The subscription closes when no observer is left, and other components read the same data from the cache:

```ts
const price = useQuery(
  trpc.price.onTick.liveOptions({ input: { symbol: 'TRPC' } }),
);
const chat = useQuery(
  trpc.chat.onMessage.streamedOptions({
    input: { room: 'general' },
    maxChunks: 100,
  }),
);
chat.data;
// (property) data: {
//     text: string;
// }[] | undefined
```

`maxChunks` keeps only the newest events. `refetchMode` says what a refetch does with the events so far: `'reset'` (the default) clears them, `'append'` keeps them and adds the new ones, and `'replace'` swaps them once the new stream ends.
