> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://v3.viem.sh/api/mcp` to find what you need.

# Use WebSocket Subscriptions

## Overview

The [`webSocket`](/docs/transports/websocket) Transport supports `eth_subscribe`, reconnects after a
closed connection, and resubscribes active watchers.

Watch Actions use subscriptions when the Client Transport supports them and polling otherwise.

## Recipes

These recipes assume the provider exposes a
[WebSocket endpoint](/docs/transports/websocket).

### Watch Block Numbers

[`block.watchNumber`](/docs/actions/public/block/watchNumber) subscribes to new block headers and
returns a watcher handle.

:::code-group
```ts twoslash [example.ts]
import { client } from './viem.config'

const watch = client.block.watchNumber() // [!code focus]

watch.onBlockNumber((blockNumber) => { // [!code focus]
  console.log(blockNumber) // [!code focus]
}) // [!code focus]

watch.onError((error) => console.error(error)) // [!code focus]

// Later:
watch.off() // [!code focus]
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
import { Client, publicActions, webSocket } from 'viem'
import { mainnet } from 'viem/chains'

export const client = Client.create({
  chain: mainnet,
  transport: webSocket('wss://eth.merkle.io/ws'),
}).extend(publicActions())
```
:::

The watcher is also an async iterable. It yields the latest update, may skip intermediate values if
the consumer is slow, and throws source errors from the loop.

:::code-group
```ts twoslash [example.ts]
import { client } from './viem.config'

const watch = client.block.watchNumber()

for await (const { blockNumber } of watch) { // [!code focus]
  console.log(blockNumber) // [!code focus]
} // [!code focus]
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
import { Client, publicActions, webSocket } from 'viem'
import { mainnet } from 'viem/chains'

export const client = Client.create({
  chain: mainnet,
  transport: webSocket('wss://eth.merkle.io/ws'),
}).extend(publicActions())
```
:::

### Watch Contract Events

Pass a typed event to [`event.watch`](/docs/actions/public/event/watch) to decode matching logs as
they arrive.

:::code-group
```ts twoslash [example.ts]
import { AbiEvent } from 'viem/utils'
import { client } from './viem.config'

const watch = client.event.watch({ // [!code focus]
  address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // [!code focus]
  event: AbiEvent.from( // [!code focus]
    'event Transfer(address indexed from, address indexed to, uint256 value)', // [!code focus]
  ), // [!code focus]
}) // [!code focus]

watch.onLogs((logs) => console.log(logs)) // [!code focus]
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
import { Client, publicActions, webSocket } from 'viem'
import { mainnet } from 'viem/chains'

export const client = Client.create({
  chain: mainnet,
  transport: webSocket('wss://eth.merkle.io/ws'),
}).extend(publicActions())
```
:::

### Tune Reconnection

Set a request timeout and reconnection policy for the provider's expected availability profile.

:::code-group
```ts twoslash [example.ts]
import { client } from './viem.config'

const watch = client.block.watchNumber()
watch.onBlockNumber(console.log)
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
import { Client, publicActions, webSocket } from 'viem'
import { mainnet } from 'viem/chains'

export const client = Client.create({
  chain: mainnet,
  transport: webSocket('wss://eth.merkle.io/ws', {
    reconnect: { maxRetries: 10, minReconnectionDelay: 2_000 }, // [!code focus]
    timeout: 20_000, // [!code focus]
  }),
}).extend(publicActions())
```
:::

## Best Practices

### Tear Down Watchers

Call `watch.off()` when a component, request, or process no longer consumes updates. This removes
listeners and releases the underlying subscription when it is no longer shared.

### Handle Gaps

Connections can drop between blocks. For workflows that cannot miss data, persist the last
processed block and backfill with a log query after reconnecting.

## See More

<Cards>
  <Card icon="lucide:activity" title="Watch Blocks and Simulate" description="Consume block streams and inspect upcoming state." to="/docs/guides/blocks-events/watch-simulate" />

  <Card icon="lucide:scroll-text" title="Query Event Logs" description="Backfill a deterministic block range." to="/docs/guides/blocks-events/logs" />
</Cards>
