Skill 05 · Sanity Best Practices
Subchapter 5.5
references/dashboard.mdMarkdown7 KBView on GitHub
The Sanity Dashboard is where an organization’s Studios and App SDK apps run. A Studio or app opts into the Dashboard beta by declaring itself with defineApplication in sanity.cli.ts. That changes how it is created, developed, and deployed. For SDK data hooks (useDocuments, , and so on), see the rule.
useDocumentapp-sdkRead sanity.cli.ts before running dev or deploy:
app: defineApplication({...}) → a Dashboard app (beta). Follow this rule.app: {organizationId, entry} with no defineApplication → a classic App SDK app. Only the Visibility and Dashboard Hooks sections apply.app key → a classic Studio. This rule does not apply.application.id from the first deploy as deployment.appId in sanity.cli.ts@sanity/sdk-react/dashboard, not @sanity/sdk-reactslug, title, and visibility in sanity.cli.ts and redeploy; config is the source of truthdefineApplication; it opts the project into the Dashboard beta--create or --external to sanity deploy for a Dashboard app (both rejected)visibility: 'disabled' on an SDK app; it makes the app unreachable (hidden from the sidebar and 404 on the direct link). Use 'unlisted'.SanityApp; Studio already provides the SDKsanity init --dashboard scaffolds a Studio or app with defineApplication. The flag is beta; if npx sanity@latest init --help does not list it, the installed CLI predates it.
# App SDK app
npx sanity@latest init --dashboard --template app-quickstart --organization <org-id> --output-path . --typescript
# Studio
npx sanity@latest init --dashboard --project <project-id> --dataset production --template clean --typescript --output-path studio--dashboard is ignored for remote templates and Next.js projects.
App SDK app:
import { defineApplication, defineCliConfig } from 'sanity/cli'
export default defineCliConfig({
app: defineApplication({
title: 'Content Reviews',
slug: 'content-reviews',
organizationId: 'your-org-id',
entry: './src/App.tsx',
}),
deployment: {
appId: 'abc123', // added after the first deploy
},
})Studio:
import { defineApplication, defineCliConfig } from 'sanity/cli'
export default defineCliConfig({
api: { projectId: 'your-project-id', dataset: 'production' },
app: defineApplication({
title: 'Marketing Studio',
slug: 'marketing-studio',
organizationId: 'your-org-id',
}),
deployment: {
autoUpdates: true,
appId: 'abc123', // added after the first deploy
},
})| Field | Notes |
|---|---|
title | Required. The name shown in the Dashboard. |
slug | Required. The app’s address in the organization: lowercase letters, numbers, and hyphens, starting with a letter and ending with a letter or number. Must be free in the organization on first deploy. Changing it and redeploying renames the address. |
organizationId | Required. The organization whose Dashboard runs the app. |
entry | SDK apps only; defaults to ./src/App.tsx. Rejected on a Studio. |
visibility | See below. |
visibility controls whether the app appears in the Dashboard sidebar. Set it inside defineApplication, or as app.visibility in a classic app config (requires sanity v6.6.0+). It is applied on deploy.
default — listed in the sidebar (the default when omitted).unlisted — hidden from the sidebar but still opens via a direct link. Not private: anyone with the link can open it.npm run dev (sanity dev) starts a local Dashboard on the configured port (3333 by default) and serves the Studio or app on the next port (3334), loaded into that Dashboard. Open the Dashboard URL the CLI prints, not the app port.
The Dashboard needs a signed-in Sanity account, so a human must complete sign-in in the browser. --load-in-dashboard is ignored; a Dashboard app already runs in the local Dashboard.
The first deploy creates the application from slug and title, then deploys to it. It works the same for Studios and apps:
npm run deploy -- --yes --jsonapplication.id from the JSON response as deployment.appId in sanity.cli.ts.deployment.appId, a later deploy fails because the slug is taken; the error includes the app ID to save.--dry-run previews without creating or uploading anything.--title overrides title for this deploy.--no-build, because the new app ID is built into the bundle.api.projectId. studioHost and --url do not apply; the address comes from slug.TERM=dumb). Use these flags rather than changing terminal settings.Deployed apps are served at https://<organizationId>.sanity.run/applications/<appId> and Studios at https://<organizationId>.sanity.run/studios/<appId>.
Hooks that talk to the Dashboard are exported from @sanity/sdk-react/dashboard (App SDK v3+). They are not in the root @sanity/sdk-react export.
| Hook | Use |
|---|---|
useNavigate | Keep the app’s router in sync with the Dashboard URL |
useNavigateToStudioDocument | Open a document in its Studio |
useOrganizationId | The organization selected in the Dashboard |
useApplications / useApplication | Apps installed in the Dashboard |
useWindowTitle | Set the browser tab title |
import { useNavigateToStudioDocument } from '@sanity/sdk-react/dashboard'In an SDK app, call them inside SanityApp, like any SDK hook.
In a Studio (sanity v6.10.0+), Studio mounts an SDK instance for the workspace, so SDK hooks work in tools, panes, and inputs without a provider. Add @sanity/sdk-react to the Studio’s dependencies to import them. Do not add SanityApp: its auth boundary would take over the Studio UI.