> 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).

# Extension API reference

Reference for defineApplication, the view and web worker helpers, unstable_defineView, unstable_defineService, and unstable_defineMediaLibrary.

The Dashboard extension API declares a Dashboard application, the views it shows, and the web workers it runs. Call the configuration helpers in `sanity.cli.ts`. Use the runtime helpers as the default export of each view or web worker source file.

> [!WARNING]
> Beta API
> The Dashboard extension API is in beta. Functions prefixed with `unstable_` can change without notice.

## Imports

- `sanity/cli`: `defineApplication`, `definePanelView`, `defineWindowView`, `defineTileView`, `defineAssetSourceView`, `defineWebWorker`, and `unstable_defineMediaLibrary`.
- `sanity/dashboard`: `unstable_defineView` and `unstable_defineService`.

## defineApplication

Declares the application. Assign the result to `app` in `defineCliConfig`.

**sanity.cli.ts**

```typescript
import {defineApplication, defineCliConfig, definePanelView, defineWebWorker} from 'sanity/cli'

export default defineCliConfig({
  app: defineApplication({
    organizationId: 'your-organization-id',
    slug: 'my-app',
    name: 'my-app',
    title: 'My app',
    entry: './src/App.tsx',
    views: [definePanelView({name: 'inbox', title: 'Inbox', src: './src/panel.tsx'})],
    webWorkers: [defineWebWorker({name: 'sync', title: 'Sync', src: './src/worker.ts'})],
  }),
})
```

#### Properties

**organizationId** (string, required)

ID of the organization that owns the app. The app runs in and deploys to this organization's Dashboard.

**slug** (string, required)

Application address and the hostname it deploys to. Use lowercase letters, numbers, and hyphens. Start with a letter and end with a letter or number.

**title** (string, required)

Label shown on the Dashboard dock button. Overrides the title in sanity.config.ts.

**name** (string)

Stable application identity. Defaults to slug. Set it explicitly so that renaming slug doesn't change the app's identity. Uses the same format as slug.

**entry** (string)

Path to the App SDK entry file, for example ./src/App.tsx. Creates the app's navigable window. Omit it for an app that only exposes views and web workers. Studios get their window automatically and don't accept entry.

**icon** (string)

Path to an SVG shown on the dock button. Overrides the icon from the app manifest or Studio configuration.

**dock** ({group, order})

Default dock placement for the app's window and its panel and window views. group is system, applications, or user. order sorts ascending within the group; anything without an order sorts as 100.

**views** (array)

Views the app exposes. Create each entry with definePanelView, defineWindowView, defineTileView, or defineAssetSourceView.

**webWorkers** (array)

Background web workers the app runs. Create each entry with defineWebWorker.

**visibility** (default | unlisted | disabled)

Dashboard visibility. Defaults to default.

`name` must be unique across the app's window, its views, and its web workers. The app's window uses the app `name`, or `slug` when `name` is omitted.

### Validation errors

The CLI validates `defineApplication` when it starts, builds, or deploys the app. It reports these errors:

- `App `organizationId` is required — pass the owning organization's ID to `defineApplication``
- `App `slug` is required — the hostname the application is created at on deploy`
- `App `slug` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric`
- ``name` must be unique across views and web workers. Rename one of the duplicates.`
- `App views for studios are not implemented yet`

## View helpers

Each helper sets the view's surface. Add the result to `views` in `defineApplication`. Every view takes these fields:

#### Properties

**name** (string, required)

Identifies the view. Must be unique within the app. Use letters, numbers, hyphens, and underscores.

**title** (string, required)

Display title.

**src** (string, required)

Path to the view's source file. For panel, tile, and asset source views, the file default-exports unstable_defineView for the same surface.

- `definePanelView({name, title, src, dock})`: Declares a dock panel. `dock` is optional and overrides the app-level `dock`.
- `defineWindowView({name, title, src, dock})`: Declares a window. `dock` is optional and overrides the app-level `dock`.
- `defineTileView({name, title, src, size, order})`: Declares a Dashboard tile. `size` is required and is `small`, `large`, or `banner`. `order` is optional and sorts tiles in ascending order.
- `defineAssetSourceView({name, title, src})`: Declares an asset source picker.

## defineWebWorker

Declares a background web worker. Add the result to `webWorkers` in `defineApplication`. It takes `name`, `title`, and `src`, with the same rules as view fields. The `src` file default-exports `unstable_defineService`.

## unstable_defineView

Import `unstable_defineView` from `sanity/dashboard` and use the result as the default export of a view's `src` file. The first argument is the surface. The second is the components for that surface.

**src/panel.tsx**

```tsx
import {unstable_defineView} from 'sanity/dashboard'

export default unstable_defineView('panel', {
  title: ({view}) => <>{view.title}</>,
  panel: ({view}) => <p>Content for {view.name}</p>,
})
```

- `panel`: `{title, panel}`. Each component receives `view`, which contains the view's `name` and `title`.
- `tile`: `{tile}`. The component receives `view`, which contains the tile's `name`, `title`, and `size`, so the tile can render for its footprint.
- `asset_source`: `{asset_source}`. The component receives the same props as a Studio asset source component (`AssetSourceComponentProps`).

Each component renders in its own island and doesn't inherit the Dashboard's context or providers. Wrap it in `SanityApp` from `@sanity/sdk-react` before using App SDK hooks.

**src/tile.tsx**

```tsx
import {unstable_defineView} from 'sanity/dashboard'

export default unstable_defineView('tile', {
  tile: ({view}) => (view.size === 'banner' ? <h2>{view.title}</h2> : <p>{view.title}</p>),
})
```

## unstable_defineService

Import `unstable_defineService` from `sanity/dashboard` and use the result as the default export of a web worker's `src` file. The first argument is the service type; `worker` is the only type. The callback runs once when the worker starts and receives `bus` and `service`. `service` contains the worker's `name` and `type`. Return a cleanup function for timers, listeners, and connections.

`bus` is the Dashboard message bus, scoped to the views of the same app:

- `bus.emit(topic, payload)`: send a message. Emit `workers.message` to reach the app's views.
- `bus.query(stateTopic)`: read a state topic's current value.
- `bus.subscribe(stateTopic, handler, {signal})`: listen to a state topic. Subscribe to `workers.post` to answer a view's post. Each message arrives as `{name, payload}` and carries `reply` and `reject`.

**src/worker.ts**

```typescript
import {unstable_defineService} from 'sanity/dashboard'

export default unstable_defineService('worker', ({bus, service}) => {
  const controller = new AbortController()

  bus.subscribe(
    'workers.post',
    (message) => message.reply(`${service.name} is running`),
    {signal: controller.signal},
  )

  return () => controller.abort()
})
```

