Please Note this project is currently in development and still has some rough edges. Use at your own risk.
A production-ready monorepo combining a TanStack React frontend with Sanity Studio, powered by Vite+.
This project is a custom fork of the original Santan Monorepo, which can be found here.
- Overview
- Quick Start
- Project Structure
- Development
- Production
- Type Generation
- Available Commands
- Documentation
- Troubleshooting
This monorepo combines a React frontend and Sanity Studio into a single, optimized workspace with:
β
Shared type system - Auto-generated Sanity types used across both apps
β
Vite+ task runner - Workspace-aware vp run for build and dev
β
Production-ready - Properly configured for deployment
β
Type-safe - Full TypeScript support throughout
β
Hot reloading - Fast development experience
Frontend (apps/frontend)
- React 19
- TanStack Router & Query
- Vite+ (Vite 8, Oxlint, Oxfmt, Vitest)
- Sanity Client
- Tailwind CSS
Studio (apps/studio)
- Sanity Studio 5
- Custom schema types
- Document preview
Shared (packages/shared)
- Auto-generated Sanity types
- Shared utilities
- Type-safe enums
- Node.js β₯ 24
- npm (comes with Node.js)
- Sanity account with a configured project
cd /path/to/santan-monorepo
npm installFrontend:
cp apps/frontend/.env.example apps/frontend/.env.localEdit apps/frontend/.env.local:
VITE_SANITY_PROJECT_ID=your_project_id
VITE_SANITY_DATASET=production
VITE_SANITY_API_VERSION=2024-01-01
SESSION_SECRET=generate_a_random_secret_here
# Contact form (see docs/CONTACT_FORM.md)
DATABASE_URL=your_neon_connection_string
ZOHO_SMTP_USER=you@yourdomain.com
ZOHO_SMTP_PASS=your_zoho_app_passwordStudio:
cp apps/studio/.env.example apps/studio/.env.localEdit apps/studio/.env.local:
SANITY_STUDIO_PROJECT_ID=your_project_id
SANITY_STUDIO_DATASET=productionnpm run devThis starts:
- π Frontend at http://localhost:3000
- π¨ Studio at http://localhost:3333
- π§ Shared package in watch mode (auto-recompiles on changes)
santan-monorepo/
βββ apps/
β βββ frontend/ # React frontend
β β βββ src/
β β β βββ routes/ # TanStack Router routes
β β β βββ components/ # React components
β β β βββ sanity/ # Sanity queries and loaders
β β β βββ types/ # Frontend-specific types
β β βββ .env.local # Environment variables (not in git)
β β βββ package.json # @santan/frontend
β β
β βββ studio/ # Sanity Studio
β βββ src/
β β βββ schemaTypes/ # Content schemas
β β βββ structure/ # Studio structure
β β βββ scripts/ # Type generation scripts
β βββ .env.local # Environment variables (not in git)
β βββ package.json # @santan/studio
β
βββ packages/
β βββ shared/ # Shared package (auto-generated types)
β βββ src/
β β βββ types/
β β β βββ sanity.types.ts # Generated Sanity types
β β β βββ sanityTypeLiterals.ts # Type literal enums
β β βββ index.ts # Main export
β βββ dist/ # Compiled output (generated)
β βββ package.json # @santan/shared
β
βββ vite.config.ts # Vite+ lint, format, test, and task config
βββ package.json # Root package with workspaces
βββ README.md # This file
βββ docs/
βββ TYPE_MIGRATION.md # Type generation guide
βββ PRODUCTION_READY.md # Production deployment guide
npm run devStarts all workspaces with hot reloading:
- Frontend dev server
- Studio dev server
- Shared package in watch mode (auto-rebuilds on changes)
# Frontend only
vp dev
# Studio only
vp run @santan/studio#dev
# Shared package only (watch mode)
vp run @santan/shared#devThe @santan/shared package contains auto-generated Sanity types:
// Import in Frontend or Studio
import { Post, Category, Author, sanityTypeLiterals } from '@santan/shared/types';
// Type-safe document checking
if (doc._type === sanityTypeLiterals.post) {
// TypeScript knows doc is Post type
console.log(doc.title, doc.slug);
}npm run buildThis builds all packages in the correct order:
- Shared package β Compiles TypeScript to JavaScript
- Studio β Builds Sanity Studio (using shared types)
- Frontend β Builds React app (using shared types)
- Frontend:
apps/frontend/.output/(Nitro/Vite output) - Studio:
apps/studio/dist/(Sanity Studio build) - Shared:
packages/shared/dist/(Compiled types)
Frontend (Vercel/Netlify):
- Root directory:
apps/frontend - Build command:
npm run build - Output directory:
apps/frontend/.outputorapps/frontend/dist
Studio (Sanity):
cd apps/studio
npm run deployOr from root:
npm run deploy --workspace=@santan/studioSee packages/shared/PRODUCTION_READY.md for complete deployment guide.
Run type generation whenever you:
- Add a new document type in Sanity Studio
- Modify existing schemas
- Change field definitions
- Update portable text configurations
cd apps/studio
npm run generate-typesWhat this does:
- Extracts Sanity schema β
schema.json - Generates TypeScript types β
packages/shared/src/types/sanity.types.ts - Extracts type literals β
packages/shared/src/types/sanityTypeLiterals.ts
The shared package automatically rebuilds (if dev mode is running), making types instantly available to both Frontend and Studio.
See TYPE_MIGRATION.md for detailed type generation workflow.
| Command | Description |
|---|---|
vp run -r --parallel dev / npm run dev |
Start all apps in development mode |
vp run -r build / npm run build |
Build all apps for production |
vp run -r type-check |
Type check all packages |
vp check |
Format, lint, and type-check (Oxfmt + Oxlint) |
vp lint / npm run lint |
Lint all packages with Oxlint |
vp fmt --write / npm run format |
Format code with Oxfmt |
npm run clean |
Clean build artifacts |
Run commands in specific packages:
# Pattern
vp run @santan/<package>#<task>
# Examples
vp dev
vp run @santan/studio#dev
vp run @santan/shared#type-check- TYPE_MIGRATION.md - Complete type generation and migration guide
- packages/shared/TYPES_README.md - Detailed shared package documentation
- packages/shared/PRODUCTION_READY.md - Production deployment guide
- GETTING_STARTED.md - Detailed setup instructions
- docs/FULLSLUG_SYSTEM.md - Sanity fullSlug system for hierarchical URLs
- docs/INDEX.md - Complete documentation index
If ports 3000 or 3333 are in use:
# Kill processes on specific ports
lsof -ti:3000 | xargs kill -9
lsof -ti:3333 | xargs kill -9
# Or kill all dev servers
pkill -f "npm run dev"Check your .env.local files:
- β
VITE_SANITY_PROJECT_IDmatches your Sanity project - β
VITE_SANITY_DATASETis correct (usually "production") - β
VITE_SANITY_API_VERSIONis valid
-
Regenerate types:
cd apps/studio npm run generate-types -
If dev mode is running, shared package should auto-rebuild
-
Otherwise, manually build:
cd packages/shared npm run build -
Restart TypeScript server in your IDE:
- VS Code:
CMD+Shift+Pβ "TypeScript: Restart TS Server" - WebStorm: Should auto-reload
- VS Code:
Ensure dependencies are installed:
npm installIf issues persist, clean and reinstall:
npm run clean
rm -rf node_modules apps/*/node_modules packages/*/node_modules
npm installEnsure the shared package is built before other packages:
vp run @santan/shared#build
vp run -r buildvp run -r build already includes @santan/shared. For a frontend-only deploy, use vp run --filter @santan/shared --filter @santan/frontend build.
β
Single clone - Get frontend and studio together
β
Shared types - Auto-generated, always in sync
β
Fast builds - Vite+ task runner across workspaces
β
Hot reloading - Changes reflect immediately
β
Type safety - Full TypeScript support
β
Optimized builds - Only rebuild what changed
β
Type-safe deployments - Compile-time type checking
β
Atomic commits - Change frontend and studio together
β
Single source of truth - One repo, one package.json
β
Easier onboarding - Clone once, everything works
β
Consistent tooling - Same linting, formatting, testing
β
Simplified CI/CD - One pipeline for everything
β
Better collaboration - See all changes in one place
- Vite+: https://viteplus.dev/guide
- Sanity: https://www.sanity.io/docs
- TanStack Router: https://tanstack.com/router
- Vite: https://vitejs.dev
MIT
Status: β
Production Ready
Last Updated: October 30, 2025