Sanity splits cleanly into two halves: the content lake (Sanity's hosted backend where your data and APIs live) and the Studio (the editing UI, which is just a React single-page application). Because the Studio is a static SPA that talks to Sanity's API over HTTPS, you don't need a server to host it. You can ship it to any static host. This guide shows how to self-host Sanity Studio as a static site instead of relying on sanity deploy.
Why self-host the Studio?
The built-in sanity deploy command publishes your Studio to a *.sanity.studio subdomain, which is convenient. But teams often want the Studio on their own domain (cms.yourcompany.com), behind their own auth/networking, or alongside their other static sites on one platform. Since the Studio is just static assets, self-hosting is straightforward.
Building the Studio
A Sanity Studio project (v3/v4) builds with the Sanity CLI:
npm install
npx sanity buildBy default this outputs static files to the dist/ directory — a standard SPA bundle of HTML, JS, and CSS. That directory is everything you need to serve.
Your sanity.config.ts holds the important bits — project ID and dataset:
import { defineConfig } from 'sanity';
import { structureTool } from 'sanity/structure';
export default defineConfig({
name: 'default',
title: 'My Studio',
projectId: 'your_project_id',
dataset: 'production',
plugins: [structureTool()],
});Deploying the static output
On PandaStack, a Sanity Studio is a static site. Connect the Git repo and the platform auto-detects the framework, runs the build, and serves dist/. If auto-detection doesn't pick the right command, set them explicitly:
- Install command:
npm install(orpnpm install/yarn/bun install— overridable) - Build command:
npx sanity build - Output directory:
dist
Static builds run in pandastack.ai microVMs, and the result is served over a CDN with automatic SSL. The same approach works on any static host — the only platform-specific config is build command and output dir.
The CORS step everyone forgets
This is the one thing that will trip you up. When you host the Studio on a custom origin (anything other than localhost or *.sanity.studio), Sanity's content lake will reject the Studio's API requests until you add that origin to your project's CORS allowlist.
Add your deployed URL in the Sanity management console (Project → API → CORS origins), or via the CLI:
npx sanity cors add https://cms.yourcompany.com --credentialsThe --credentials flag is required because the Studio sends authenticated requests (your editors' login sessions). Without it, login silently fails. Add both your production domain and any preview/staging domains you use.
SPA routing
The Studio is a client-side-routed SPA. Deep links like /desk/post;abc123 need to fall back to index.html or you'll get 404s on refresh. Most static hosts handle SPA fallback automatically when they detect an SPA; if yours needs explicit config, set a catch-all rewrite to /index.html. On PandaStack the static site serving handles SPA fallback, so client-side routes resolve on direct navigation.
Custom domain and SSL
Point a subdomain at your static deployment and the platform provisions SSL automatically. On PandaStack you add the custom domain in the dashboard, update your DNS (CNAME), and SSL is issued automatically. Remember to add that same domain to Sanity's CORS allowlist (see above) — the two steps go together.
Environment and dataset separation
A common setup is separate datasets per environment:
| Environment | Dataset | Studio URL |
|---|---|---|
| Production | production | cms.yourcompany.com |
| Staging | staging | cms-staging.yourcompany.com |
You can drive the dataset from an environment variable at build time:
dataset: process.env.SANITY_STUDIO_DATASET || 'production',Sanity Studio env vars must be prefixed SANITY_STUDIO_ to be exposed to the client bundle — set these in your build environment. Build each environment as its own static deployment with the appropriate var.
A note on the front-end vs the Studio
Don't confuse the Studio (editing UI) with your actual website (the front-end that reads content). They're separate deployments. The Studio is where editors work; your Next.js/Astro/etc. site reads from the same content lake via the Sanity client. Both can live as static (or container) deployments on the same platform, which keeps everything in one dashboard.
References
- Sanity Studio deployment docs: https://www.sanity.io/docs/deployment
- Sanity CORS configuration: https://www.sanity.io/docs/cors
- Sanity CLI reference: https://www.sanity.io/docs/cli
- sanity.config.ts reference: https://www.sanity.io/docs/configuration
- Sanity client: https://www.sanity.io/docs/js-client
---
Want your Sanity Studio on your own domain with automatic SSL? PandaStack deploys static sites from your Git repo for free, with custom domains and a CDN. Start at https://dashboard.pandastack.io — just remember to add the domain to Sanity's CORS allowlist.