# Scopes
URL: /tap/docs/store/scopes

Named, independent units of state in your store.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

A **scope** is a named slot in your store. Instead of putting everything in one place, you split your app's state into independent units — each with a name, its own methods, and its own state.

A chat app might have three scopes that nest naturally:

```
App              ← threadList scope
  └ ThreadView   ← thread scope
    └ Message    ← message scope
```

Each level of the tree adds a scope. Components at any level can access the scopes above them.

## Registering scopes

You declare what scopes exist by augmenting `ScopeRegistry`:

```
import "@assistant-ui/store";

declare module "@assistant-ui/store" {
  interface ScopeRegistry {
    threadList: {
      methods: {
        getState: () => { threadIds: string[] };
      };
    };
    thread: {
      methods: {
        getState: () => { messages: { role: string; content: string }[] };
      };
    };
    message: {
      methods: {
        getState: () => { role: string; content: string };
      };
    };
  }
}
```

This is a **type-level** declaration — no runtime code. It tells TypeScript what scopes exist and what shape each one has. Every Store hook uses this for autocomplete and type checking.

Because `ScopeRegistry` uses TypeScript module augmentation, different packages can each register their own scopes. A `@my-org/chat` package can register `thread` and `message`, while `@my-org/analytics` registers `analytics` — and your app can access all of them through the same `useAui` hook.

## Filling scopes

You fill scopes with an `AuiProvider` whose `config` is built with `AuiConfig(...)`:

```
AuiConfig({
  threadList: ThreadListResource(),
});
```

Each key matches a name from `ScopeRegistry`. Each value is a Tap resource element that implements the scope's methods.

At the top level, `config` alone creates the store:

```
const App = () => {
  const config = AuiConfig({ threadList: ThreadListResource() });
  return (
    <AuiProvider config={config}>
      <ThreadView />
    </AuiProvider>
  );
};
```

Now any component inside `App` can call `useAui()` to get the store with the `threadList` scope filled.

You don't have to fill every scope at once. Each level of your tree can extend the store with its own scopes — a nested provider passes `extends={aui}` to extend the parent (or `extends={null}` to isolate):

```
const ThreadView = () => {
  // extends the parent store with a thread scope
  const aui = useAui();
  const config = AuiConfig({ thread: ThreadResource() });
  return (
    <AuiProvider extends={aui} config={config}>
      <MessageList />
    </AuiProvider>
  );
};
```

The nested provider takes the parent store (which already has `threadList`) and extends it with `thread`. Components inside `ThreadView` can access both scopes.

- [Methods](/tap/docs/store/methods) —

  Access scope methods with useAui.

- [State](/tap/docs/store/state) —

  Subscribe to state changes with useAuiState.