Guide

Getting started

End-to-end setup for the ACTTA Studio Sanity Starter — from pulling the template into a new project through a live Vercel deploy with preview, webhooks, analytics, and form notifications.

Full docs (navigable): sanity-starter-guide.vercel.app

Prerequisites

Optional later:

  • Umami Cloud — analytics
  • Resend — contact form email notifications
  • Mux — video uploads in Studio

1. Get the starter

The starter lives in a template repository (this repo). You do not fork it for a new client project — you degit a clean copy into your own folder or repo.

tiged excludes folders listed in .degitignore — notably guide-site/, which is only for the deployed documentation app in the template repo.

npx tiged nonsoanetoh/sanity-starter --mode=git my-project
cd my-project

Template repo: github.com/nonsoanetoh/sanity-starter

What you get:

IncludedExcluded
Next.js app + embedded Studioguide-site/ (separate Vercel project in the template repo)
docs/ markdown for local referenceTemplate git history
Seed data, scripts, schemas

Use the hosted guide for navigation and search: sanity-starter-guide.vercel.app. You do not need guide-site/ in your project.

Initialize your own git repo when ready:

git init
git add .
git commit -m "Initial commit from ACTTA Studio Sanity Starter"

Push to GitHub (or GitLab) before connecting Vercel in step 9.

Alternative: git clone

Use this if you are contributing to the template or working from a fork that should keep full history:

git clone https://github.com/nonsoanetoh/sanity-starter.git
cd sanity-starter

If you cloned the full template repo, you can delete guide-site/ locally — it is not required to run the starter.

Vanilla degit (no ignore support)

npx degit nonsoanetoh/sanity-starter my-project
cd my-project
rm -rf guide-site   # not needed in your project

Prefer tiged so guide-site/ is never downloaded.


2. Install dependencies

pnpm install

Lefthook installs git hooks automatically via the prepare script.


3. Create your Sanity project

If you already have a project, skip to step 4.

  1. Go to sanity.io/manage
  2. Create a project (or use an existing one)
  3. Note your Project ID (e.g. r94x15fe)
  4. Use dataset production (default)

4. Local environment

Run the setup wizard — it creates .env.local from .env.example if needed:

pnpm sanity:project-setup

Edit .env.local and fill in the required values:

NEXT_PUBLIC_URL=http://localhost:3000
NEXT_PUBLIC_SANITY_PROJECT_ID=your-project-id
NEXT_PUBLIC_SANITY_DATASET=production
NEXT_PUBLIC_SANITY_API_VERSION=2024-01-01
NEXT_PUBLIC_SANITY_STUDIO_BASE_PATH=/studio
VariableRequiredPurpose
NEXT_PUBLIC_SANITY_PROJECT_IDYesSanity project ID (browser + server)
NEXT_PUBLIC_SANITY_DATASETYesUsually production
NEXT_PUBLIC_URLYesSite URL for SEO and Presentation Tool
NEXT_PUBLIC_SANITY_STUDIO_BASE_PATHNoDefaults to /studio

NEXT_PUBLIC_* vars are required for embedded Studio — the browser cannot read SANITY_STUDIO_*.


5. Sanity API tokens

Create tokens at sanity.io/manage → your project → API → Tokens.

Viewer token (draft preview)

  1. Add API token → name it next-viewer
  2. Role: Viewer
  3. Copy the token into .env.local:
SANITY_API_VIEW_TOKEN=your-viewer-token

Enables draft mode, live preview in Presentation, and SanityLive subscriptions locally.

Editor token (contact forms)

  1. Add API token → name it next-editor
  2. Role: Editor
  3. Copy into .env.local:
SANITY_API_EDIT_TOKEN=your-editor-token

Required for contact form submissions to write contactFormSubmission documents in Sanity.


6. CORS origins (local)

In Sanity → API → CORS origins, add:

OriginAllow credentials
http://localhost:3000Yes

Credentials are required for live preview and draft mode.


7. Import seed content

Load the starter homepage, site settings, and sample content:

pnpm sanity:project-setup --import-seed
# or
pnpm sanity:dataset-import

Visit:


8. Verify locally

  1. Homepage loads at /
  2. Studio loads at /studio — sign in with your Sanity account
  3. Edit a page section → open Presentation → confirm live preview updates without a full refresh — see features/draft-mode.md
  4. Submit the contact form (requires SANITY_API_EDIT_TOKEN) — see features/contact-forms.md

9. Deploy to Vercel

Connect the repo

  1. Import the project at vercel.com/new
  2. Settings:
SettingValue
FrameworkNext.js
Build commandpnpm build
Install commandpnpm install

Environment variables

Copy all values from .env.local into Vercel → Settings → Environment Variables.

Required for production:

VariableExample
NEXT_PUBLIC_SANITY_PROJECT_IDyour project ID
NEXT_PUBLIC_SANITY_DATASETproduction
NEXT_PUBLIC_URLhttps://your-app.vercel.app
SANITY_API_VIEW_TOKENViewer token
SANITY_API_EDIT_TOKENEditor token
SANITY_REVALIDATE_SECRETrandom secret (see step 10)

Use the base Vercel URL for NEXT_PUBLIC_URL — no trailing slash, no /studio.

Deploy once to get your live URL, then update NEXT_PUBLIC_URL if needed and redeploy.


10. Revalidation webhook

The webhook clears Next.js cache when content is published so the live site updates within seconds.

Deploy-first order

Do these in order — creating the webhook before the site is live will cause failed test deliveries:

  1. Deploy to Vercel and confirm the site loads
  2. Set NEXT_PUBLIC_URL to your base Vercel URL and redeploy
  3. Generate and add SANITY_REVALIDATE_SECRET to Vercel, then redeploy again
  4. Create the webhook in Sanity pointing at your live /api/revalidate URL

Draft and preview changes do not use the webhook — only published content triggers revalidation.

Generate a secret

openssl rand -base64 32

Add to Vercel and .env.local:

SANITY_REVALIDATE_SECRET=your-generated-secret

Redeploy after adding the env var — the running app must have the secret at build/runtime.

Create the webhook in Sanity

sanity.io/manage → your project → API → Webhooks → Create webhook

FieldValue
URLhttps://your-app.vercel.app/api/revalidate
Datasetproduction
Trigger onCreate, Update, Delete
Projection{ _id, _type, "uri": uri.current, "slug": slug.current }
Secretsame value as SANITY_REVALIDATE_SECRET

Test the webhook from Sanity — expect { "revalidated": true, "tags": [...] }.

Then publish a visible change in Studio and confirm the production site updates within a few seconds.

Troubleshooting

ResponseCauseFix
501 Revalidation not configuredSANITY_REVALIDATE_SECRET not set on Vercel, or no redeploy since adding itAdd env var and redeploy
401 Invalid signatureSecret mismatch between Vercel and Sanity webhookCopy the exact same secret to both places
Connection / timeout errorWrong URL, site not deployed, or typo in domainVerify URL loads; use base domain + /api/revalidate

Site-wide Basic Auth does not block /api/revalidate — webhooks work even when the public site is gated.

See also deployment/vercel.md and faq.md.


11. CORS origins (production)

Add your deploy URL in Sanity → API → CORS origins:

OriginAllow credentials
https://your-app.vercel.appYes

Use the base URL only — not /studio.


12. Umami analytics (optional)

Privacy-friendly page analytics. The script only loads when configured.

  1. Sign up at cloud.umami.is
  2. Settings → Websites → Add website
  3. Domain: your-app.vercel.app (no https://, no trailing slash)
  4. Copy the Website ID

Add to Vercel (and .env.local for local testing):

NEXT_PUBLIC_UMAMI_WEBSITE_ID=your-website-id
NEXT_PUBLIC_UMAMI_HOST=https://cloud.umami.is

Redeploy — NEXT_PUBLIC_* vars are baked in at build time.

Verify: DevTools → Network → cloud.umami.is/script.js loads on your site.

See features/umami-tracking.md.


13. Resend email notifications (optional)

Contact forms always save to Sanity when SANITY_API_EDIT_TOKEN is set. Email notifications are optional.

Resend setup

  1. Create an account at resend.com
  2. Add and verify a sending domain (or use Resend's test domain for development)
  3. Create an API key

Add to Vercel and .env.local:

RESEND_API_KEY=re_xxxxxxxx
RESEND_EMAIL_FROM=notifications@yourdomain.com

Studio configuration

In Studio → Site → Forms, add one or more Form notification emails.

When both Resend env vars are set and recipients are configured, successful form submissions trigger an email in addition to the Sanity document.

See features/contact-forms.md.


14. Staging gate with Basic Auth (optional)

For pre-launch review on Vercel:

BASIC_AUTH_USERNAME=your-user
BASIC_AUTH_PASSWORD=your-password

Then in Studio → Site → Security, enable Enable site-wide Basic Auth.

Env vars alone do not gate the site — the CMS toggle must be on. Studio (/studio) and /api/* routes stay accessible without auth.

See features/basic-auth.md.


15. Mux video (optional)

For video uploads in Studio:

  1. Create a Mux account and generate API access tokens
  2. Add to .env.local (Studio only — not needed on the frontend):
SANITY_STUDIO_MUX_TOKEN_ID=your-mux-token-id
SANITY_STUDIO_MUX_TOKEN_SECRET=your-mux-token-secret

Final checklist

GET THE STARTER
  ☐ tiged into new folder (or git clone if contributing to template)
  ☐ git init + push to your remote (before Vercel)
  ☐ pnpm install

LOCAL
  ☐ pnpm install
  ☐ .env.local filled in (project ID, dataset, URL)
  ☐ Viewer token → SANITY_API_VIEW_TOKEN
  ☐ Editor token → SANITY_API_EDIT_TOKEN
  ☐ CORS: http://localhost:3000 (credentials enabled)
  ☐ Seed imported
  ☐ pnpm dev — site and Studio work
  ☐ Presentation live preview works

VERCEL
  ☐ Repo connected, deploy succeeds
  ☐ All env vars copied to Vercel
  ☐ NEXT_PUBLIC_URL = base Vercel URL
  ☐ Redeployed after env changes

SANITY (PRODUCTION)
  ☐ CORS: production URL (credentials enabled)
  ☐ Webhook → /api/revalidate with matching secret
  ☐ Publish test → live site updates

OPTIONAL
  ☐ Umami website ID in NEXT_PUBLIC_UMAMI_WEBSITE_ID
  ☐ Resend API key + from address + notification emails in Site
  ☐ Basic Auth env vars + toggle in Site → Security
  ☐ Mux tokens for video uploads

Next steps