> For AI agents: the complete Sanity documentation index is available at [https://www.sanity.io/docs/llms.txt](https://www.sanity.io/docs/llms.txt).

# Talk to your app's worker

Post messages from your app's views to one of its web workers with useWorker, and listen for what the worker sends back.

A web worker keeps running while the Dashboard is open, even when nobody is looking at your app. `useWorker` from `@sanity/sdk-react/dashboard` connects one of your app's views to one of its workers: post a message and await the worker's reply, or subscribe to the messages the worker sends out.

> [!WARNING]
> Beta API
> `useWorker` and the worker message bus can change without notice.

## Connect to a worker

**src/Feed.tsx**

```tsx
import {useWorker} from '@sanity/sdk-react/dashboard'

const feed = useWorker<{payload: FeedRequest; reply: FeedSnapshot; message: FeedUpdate}>('feed')
```

The argument is the worker's `name` from `defineWebWorker`. The type parameter describes the three message shapes: `payload` is what the view posts, `reply` is what the worker returns, and `message` is what the worker emits on its own.

## Post and await a reply

**src/Feed.tsx**

```tsx
const snapshot = await feed.post({cursor: null}, {signal, timeout: 5_000})
```

`feed.post(payload, {signal, timeout})` returns a Promise that resolves with the worker's reply. If the worker never replies, the Promise rejects with a `MessageBusError`:

- `NO_RESPONDER`: the worker isn't running.
- `TIMEOUT`: the worker didn't reply in time.
- `ABORTED`: the `signal` was aborted.
- `REFUSED`: the worker called `message.reject(reason)`.
- `HANDLER_THREW`: the worker's handler threw.

## Listen for the worker's messages

**src/Feed.tsx**

```tsx
useEffect(() => feed.subscribe((update) => setItems(update.items)), [feed])
```

`feed.subscribe(handler)` returns an unsubscribe function, so you can return it straight from an effect.

## Subscribe first, then post

Messages aren't stored. A view only receives what the worker sends while the view is listening, so subscribe first and then post to get a snapshot of the worker's current state.

The worker sends a `welcome` message when it starts. Listening views post again when they receive it, so a view that connected before the worker was ready fills in as soon as it is.

The connection doesn't depend on the view being visible. A panel or tile keeps posting and receiving while it is mounted, whether or not it is on screen.

## Next steps

[Run a background service](https://www.sanity.io/docs/dashboard-beta/run-a-background-service)
Define a worker and answer your app's views.

[Interact with the Dashboard](https://www.sanity.io/docs/dashboard-beta/interact-with-the-dashboard)
Use App SDK hooks from your app, panels, and tiles.

[Extension API reference](https://www.sanity.io/docs/dashboard-beta/extension-api-reference)
Look up unstable_defineService and the bus methods.

