Deploy
Host the site on Cloudflare Pages, Netlify, Docker or Node, set the right environment values and connect your domain.
Choose a render mode
Set RENDER_MODE in .env. The default suits most sites.
| Mode | Pages are built | Hosting | When |
|---|---|---|---|
| static (default) | at build time | Cloudflare Pages, Netlify, nginx | most sites |
| hybrid | at build time, plus server endpoints | Node.js | the site needs its own server endpoints, such as a contact form |
| server | on each request | Node.js behind a CDN | thousands of pages, or content that must update within minutes |
The page code is the same in every mode. The template's README.md says if it needs a server.
Build locally first
pnpm build
This needs your Sanity settings from Connect Sanity. If it passes on your computer, the host will build the same output.
Static: Cloudflare Pages or Netlify
Connect your private repository to the host and use these build settings:
| Setting | Value |
|---|---|
| Build command | pnpm build |
| Output directory | dist/client |
| Node.js version | 22 |
Add the build environment values. Copy them from your .env:
PUBLIC_SITE_URL=https://example.com
PUBLIC_SANITY_PROJECT_ID=abc12345
PUBLIC_SANITY_DATASET=production
PUBLIC_SANITY_API_VERSION=2026-08-01
The build writes the host's redirect and header files (_redirects, _headers) for you, so don't edit them by hand.
Static: Docker (nginx)
docker build -f Dockerfile.static \
--build-arg PUBLIC_SITE_URL=https://example.com \
--build-arg PUBLIC_SANITY_PROJECT_ID=abc12345 \
-t my-site .
docker run -p 8080:8080 my-site
The image runs nginx without root privileges on port 8080. Its health check is /api/health.
Hybrid or server: Docker (Node.js)
docker build -f Dockerfile.node \
--build-arg RENDER_MODE=hybrid \
--build-arg PUBLIC_SITE_URL=https://example.com \
--build-arg PUBLIC_SANITY_PROJECT_ID=abc12345 \
-t my-site .
docker run -p 3000:3000 my-site
The server listens on port 3000 and its health check is /api/health. Give the container a stop grace period of at least 10 seconds so requests in progress can finish during a deploy.
Without Docker, build and start it directly:
pnpm build
HOST=0.0.0.0 pnpm start
Values that are fixed at build time
RENDER_MODE and every value that starts with PUBLIC_ are built into the site. On platforms that separate build and runtime settings, add them as build values. If you only set them at runtime, the build silently uses the defaults. After changing one, rebuild.
Publish and rebuild
A static site shows new content after it is rebuilt. To rebuild on every publish:
- Create a deploy hook in your host (Cloudflare Pages and Netlify both provide one) and copy its URL.
- In sanity.io/manage, open your project → API → Webhooks and add a webhook:
- URL: the deploy hook
- Trigger on: create, update, delete
- Drafts: off
- Projection:
{_id, _type}
For hybrid and server, point the webhook at https://example.com/api/revalidate instead and set its secret to the SANITY_WEBHOOK_SECRET from your .env. In hybrid, also set DEPLOY_HOOK_URL on the server so a publish triggers a rebuild. The comments in .env.example list the values for each host.
Connect your domain
- Add the domain in your host's settings and follow its DNS instructions.
- Set
PUBLIC_SITE_URLto the domain, withhttps://, and rebuild. - Let Sanity accept requests from the domain:
pnpm sanity:setup --project abc12345 --site-url https://example.com
Contact forms on a static site
A static site has no server endpoint of its own. If your template includes a contact form, its README.md and .env.example show the options: send the form to an external form service, or switch to RENDER_MODE=hybrid.