# How to use streaming in AppKit (https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/streaming/content.md)



Streaming in AppKit is a per-network subscription layer that turns provider events into live balance, jetton, and transaction updates for the app.

## Live provider signals [#live-provider-signals]

Streaming complements point-in-time API reads. Instead of asking an API client for the latest state after every interaction, the app subscribes to changes for an address on a specific network.

The `StreamingManager` coordinates providers by `chainId`. A streaming provider implements one transport for one network, such as a TON Center WebSocket stream. Registering another provider for the same network replaces the previous stream for that chain. Provider registration is covered on [Use providers → How they are registered](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/providers/content.md).

## Subscription lifetime [#subscription-lifetime]

A subscription belongs to the consumer that created it. Balance, jetton, transaction, and combined watches all return an unsubscribe function, and that function is the app's handle for stopping updates when a component unmounts or a flow ends.

When the underlying transport drops and reconnects, the SDK does not replay events from the gap. Updates resume from the provider's current view of the chain, so any intermediate transitions on a watched address are lost.

## Before you begin [#before-you-begin]

You need a connected wallet (for the default-address `useWatch*` hooks) and a streaming provider registered on the AppKit instance. See [Use providers → How they are registered](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/providers/content.md).

## Watch from React [#watch-from-react]

Mount watch hooks near the part of the tree that needs live data. The hook keeps related query caches fresh while it is mounted; passing `onChange` adds a side-effect callback on top of the cache refresh. Every React watch hook accepts an optional `{ onChange?, network? }`, and the `*ByAddress` variants additionally accept an `address`.

Mount as many watchers as you need. The SDK deduplicates subscribers by network, address, and type, so multiple hook instances watching the same data share one underlying socket subscription. There is no benefit to lifting watchers into a single root component just to share them — pull them into the component that actually needs the live value.

### Watch a balance [#watch-a-balance]

`useWatchBalance` refreshes the cache that `useBalance` reads. `useBalance` is a TanStack Query result (`{ data, isLoading, isError, refetch, ... }`); `data` is the formatted Gram balance as a string when present.

```tsx
import { useBalance, useWatchBalance } from '@ton/appkit-react';

export function LiveBalance() {
  useWatchBalance();
  const balance = useBalance();

  return <span>{balance.data ?? '0'} Gram</span>;
}
```

### Watch transactions [#watch-transactions]

`useWatchTransactions` reacts to transaction lifecycle changes. The update payload exposes `traceHash` and `status` (`'pending' | 'confirmed' | 'finalized' | 'invalidated'`). Plug in your own notification or state-update logic; the SDK does not pick a UI primitive for you.

```tsx
import { useWatchTransactions } from '@ton/appkit-react';

export function FinalizedLog() {
  useWatchTransactions({
    onChange: (update) => {
      if (update.status === 'finalized' && update.traceHash) {
        console.log('finalized', update.traceHash);
      }
    },
  });
  return null;
}
```

### Watch by address [#watch-by-address]

For receipt screens, admin tooling, or order-tracking widgets, subscribe to an address that may not be the connected wallet. The `*ByAddress` hooks take an `address` and keep the matching `useBalanceByAddress` / cache fresh while also delivering each update to `onChange`.

```tsx
import {
  useBalanceByAddress,
  useWatchBalanceByAddress,
  useWatchTransactionsByAddress,
} from '@ton/appkit-react';

export function OrderTracker({ orderAddress }: { orderAddress: string }) {
  useWatchBalanceByAddress({
    address: orderAddress,
    onChange: (update) => {
      if (update.status === 'finalized') {
        console.log('order balance', update.balance);
      }
    },
  });
  const balance = useBalanceByAddress({ address: orderAddress });

  useWatchTransactionsByAddress({
    address: orderAddress,
    onChange: (update) => {
      if (update.status === 'finalized') {
        console.log('finalized txs', update.transactions.length);
      }
    },
  });

  return <span>{balance.data ?? '0'} Gram</span>;
}
```

### Watch jettons [#watch-jettons]

`useWatchJettons` pushes jetton balance changes for the connected wallet. Each update covers one jetton (`masterAddress`, `walletAddress`, `ownerAddress`, `rawBalance`) and may include `decimals` and a formatted `balance` when the jetton's decimals are known.

```tsx
import { useWatchJettons } from '@ton/appkit-react';

export function PortfolioWatcher() {
  useWatchJettons({
    onChange: (update) => {
      if (update.status === 'finalized' && update.balance !== undefined) {
        console.log(update.masterAddress, update.balance);
      }
    },
  });
  return null;
}
```

## From vanilla code [#from-vanilla-code]

A non-React consumer keeps the unsubscribe function returned by each `watch*` action and calls it when the screen or process no longer needs updates.

```ts
import { watchBalance } from '@ton/appkit';

const unsubscribe = watchBalance(appKit, {
  onChange: (update) => {
    console.log(update.address, update.balance);
  },
});

unsubscribe();
```

The same shape applies to `watchBalanceByAddress`, `watchJettons`, `watchJettonsByAddress`, `watchTransactions`, and `watchTransactionsByAddress`. Each takes `{ network?, onChange }` (and `address` on the `*ByAddress` variants) and returns an unsubscribe function.

## Connection state [#connection-state]

Connection state describes transport health, not chain finality. A websocket can disconnect and reconnect while the underlying account state continues to change.

After reconnect, updates may resume from the provider's current view of the chain. A flow that needs certainty should refresh from the configured API client before it changes product state.

`appKit.streamingManager.onConnectionChange(network, callback)` registers a listener for the WebSocket connection state of one network. The callback receives `connected: boolean`; the method returns an unsubscribe function. Pair it with `useNetwork` so the listener follows the currently selected network.

```tsx
import { useEffect, useState } from 'react';
import { useAppKit, useNetwork } from '@ton/appkit-react';

export function StreamingHealth() {
  const appKit = useAppKit();
  const network = useNetwork();
  const [connected, setConnected] = useState(true);

  useEffect(() => {
    if (!network) return;
    return appKit.streamingManager.onConnectionChange(network, setConnected);
  }, [appKit, network]);

  return <span>{connected ? 'live' : 'reconnecting'}</span>;
}
```

## Tips [#tips]

* Store every unsubscribe function and call it when the consumer goes away.
* Call `hasStreamingProvider` before subscribing on a network that may not have a provider registered, and degrade gracefully when it returns `false`.
* Treat streaming updates as best-effort UI refresh signals. Reconcile with a point-in-time read before changing product state that depends on correctness.
* `update.status` carries one of `'pending' | 'confirmed' | 'finalized' | 'invalidated'` on every streaming payload. Only `'finalized'` is irreversible; treat the others as intermediate.

## Related pages [#related-pages]

* [Use providers → How they are registered](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/providers/content.md)
* [Configure networks](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/networks/content.md)
* [Read balances](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/read-balances/content.md)
