This is the monorepo for the Chimborazo Park Conservancy website and Sanity CMS integration.
chimborazo-park-conservancy/
βββ apps/
β βββ web/ # Main website (TanStack Start + React)
β βββ studio/ # Sanity Studio CMS
βββ packages/
β βββ sanity-config/ # Shared Sanity schemas, queries, and types
βββ turbo.json # Turborepo configuration
βββ package.json # Root package.json with workspaces
- Node.js >= 22.0.0 (enforced via
.nvmrcandenginesfield) - pnpm >= 8.0.0 (package manager)
- A Sanity account (sign up at sanity.io)
Note: If you use nvm, run nvm use in the project root to automatically switch to Node 22.
-
Clone the repository (if you haven't already)
-
Install pnpm (if not already installed)
npm install -g pnpm
-
Install dependencies
pnpm install
-
Set up Sanity Project
- Create a new project at sanity.io/manage
- Note your Project ID and Dataset name (usually "production")
- Create an API token with Editor permissions at:
https://sanity.io/manage/project/[YOUR_PROJECT_ID]/api
-
Configure environment variables
This project uses T3 Env for type-safe environment variable validation. Each workspace has its own
.env.examplefile showing required variables.For the web app (
apps/web/.env):# Copy the example file cp apps/web/.env.example apps/web/.envThen edit
apps/web/.envwith your values:# Required - Sanity CMS Configuration VITE_SANITY_PROJECT_ID=your_project_id_here VITE_SANITY_DATASET=production VITE_SANITY_API_VERSION=2024-01-01 # Optional - Server-side only (not exposed to browser) SANITY_API_TOKEN=your_api_token_here ANTHROPIC_API_KEY=your_anthropic_key_here
For the Studio (
apps/studio/.env):# Copy the example file cp apps/studio/.env.example apps/studio/.envThen edit
apps/studio/.envwith your values:# Required - Sanity CMS Configuration SANITY_STUDIO_PROJECT_ID=your_project_id_here SANITY_STUDIO_DATASET=production SANITY_STUDIO_API_VERSION=2024-01-01 # Optional - defaults provided SANITY_STUDIO_PREVIEW_URL=http://localhost:3001 SANITY_STUDIO_API_URL=http://localhost:3001/api/generate-metadata
Environment Variable Validation:
- All environment variables are validated at build/startup time using Zod schemas
- Type errors will occur if required variables are missing or invalid
- See
apps/next-web/src/env.tsandapps/studio/src/env.tsfor validation schemas - Shared Sanity schemas are defined in
packages/sanity-config/src/env-schema.ts
pnpm run devThis will start:
- Web app on http://localhost:3001
- Sanity Studio on http://localhost:3333
Web app only:
pnpm --filter @chimborazo/next-web dev
# or
cd apps/next-web && pnpm run devSanity Studio only:
pnpm --filter @chimborazo/studio dev
# or
cd apps/studio && pnpm run devpnpm run buildpnpm --filter @chimborazo/web build
pnpm --filter @chimborazo/studio buildpnpm run testpnpm run lintpnpm run type-checkpnpm run formatThe main website built with:
- TanStack Start - Full-stack React framework with SSR
- TanStack Router - File-based routing
- TanStack Query - Data fetching and caching
- Tailwind CSS v4 - Utility-first styling
- Sanity Client - CMS integration
- T3 Env - Type-safe environment variable validation
Key files:
apps/web/src/lib/sanity.ts- Sanity client configurationapps/web/src/env.ts- Environment variable validation with T3 Envapps/web/netlify.toml- Netlify deployment config
Environment variables:
- Client-side (VITE_ prefix):
VITE_SANITY_PROJECT_ID,VITE_SANITY_DATASET,VITE_SANITY_API_VERSION - Server-side only:
SANITY_API_TOKEN,ANTHROPIC_API_KEY,NETLIFY_AUTH_TOKEN, etc.
Sanity Studio for content management.
Features:
- Event management with portable text editor
- Media library with categorization
- Live preview integration (Presentation tool)
- AI-powered image metadata generation
- Custom branding
- T3 Env for environment variable validation
Key files:
apps/studio/sanity.config.ts- Studio configurationapps/studio/src/env.ts- Environment variable validation with T3 Envapps/studio/netlify.toml- Netlify deployment config
Environment variables:
- All use
SANITY_STUDIO_*prefix:SANITY_STUDIO_PROJECT_ID,SANITY_STUDIO_DATASET,SANITY_STUDIO_PREVIEW_URL, etc.
Shared package containing:
- Schemas - Sanity document schemas (event, mediaImage)
- Queries - GROQ queries for fetching data
- Client utilities - Sanity client creation and image URL builders
- TypeScript types - Generated types for type-safe CMS integration
- Environment schemas - Shared Zod schemas for Sanity env vars (used by both web and studio)
Usage in web app:
import { sanityClient, urlForImage } from '@/lib/sanity'
import { allEventsQuery, eventBySlugQuery } from '@chimborazo/sanity-config'
// Fetch all events
const events = await sanityClient.fetch(allEventsQuery)
// Get image URL with transformations
const imageUrl = urlForImage(event.heroImage)
.width(800)
.height(600)
.url()-
Connect your repo to Netlify
-
Configure build settings:
- Build command:
pnpm run build --filter=@chimborazo/web - Publish directory:
apps/web/dist/client - Functions directory:
apps/web/.netlify/functions
- Build command:
-
Add environment variables in Netlify Dashboard:
Required:
VITE_SANITY_PROJECT_ID- Your Sanity project IDVITE_SANITY_DATASET- Dataset name (usually "production")VITE_SANITY_API_VERSION- API version (e.g., "2024-01-01")
Optional (server-side only):
SANITY_API_TOKEN- For mutations and preview modeANTHROPIC_API_KEY- For AI metadata generationNETLIFY_AUTH_TOKEN- For cache purgingNETLIFY_SITE_ID- For cache purgingSANITY_WEBHOOK_SECRET- For webhook validation
Note: All env vars are validated using T3 Env. Missing required vars will cause build failures with clear error messages.
-
Deploy! π
-
Create a new Netlify site for the Studio
-
Configure build settings:
- Build command:
pnpm run build --filter=@chimborazo/studio - Publish directory:
apps/studio/dist
- Build command:
-
Add environment variables:
Required:
SANITY_STUDIO_PROJECT_ID- Your Sanity project IDSANITY_STUDIO_DATASET- Dataset name (usually "production")SANITY_STUDIO_API_VERSION- API version (e.g., "2024-01-01")
Optional (with defaults):
SANITY_STUDIO_PREVIEW_URL- Your production web app URL (default: http://localhost:3001)SANITY_STUDIO_API_URL- API endpoint for AI metadata (default: http://localhost:3001/api/generate-metadata)
Note: All env vars are validated using T3 Env. The Studio will fail to build if required vars are missing.
-
Set custom domain (e.g.,
studio.chimborazopark.org) -
Add CORS origin in Sanity project settings:
- Go to sanity.io/manage
- Navigate to your project > API > CORS Origins
- Add your Studio URL (e.g.,
https://studio.chimborazopark.org)
{
title: string
slug: slug
description: text
heroImage: image { alt, caption }
date: date
time: string
location: string
body: portableText[] // Rich text content
featured: boolean
publishedAt: datetime
}{
title: string
image: image { alt, caption, metadata }
category: 'park-views' | 'events' | 'nature' | 'community' | 'history'
featured: boolean
uploadedAt: datetime
}# Clean all build artifacts and reinstall
pnpm run clean
rm -rf node_modules pnpm-lock.yaml
pnpm install# Rebuild the sanity-config package
pnpm --filter @chimborazo/sanity-config build- Ensure
SANITY_STUDIO_PROJECT_IDis set correctly in.env - Check that your Sanity project exists at sanity.io/manage
- Verify CORS origins are configured
- Check for T3 Env validation errors in the console
# T3 Env will show clear errors if variables are missing or invalid
# Example: "VITE_SANITY_PROJECT_ID is required but was not set"
# Check your .env files match the .env.example templates
# Validation schemas are in:
# - apps/web/src/env.ts
# - apps/studio/src/env.ts
# - packages/sanity-config/src/env-schema.ts- TanStack Start Documentation
- Sanity Documentation
- Turborepo Documentation
- Netlify Documentation
- T3 Env Documentation - Type-safe environment variable validation
The monorepo infrastructure is set up! Here's what remains:
-
Migrate existing content to Sanity
- Events from
apps/web/src/data/events.ts - Media from Netlify Blobs
- Events from
-
Update web app to fetch from Sanity
- Replace static data with Sanity queries
- Update components to render Sanity data
-
Set up live preview (Presentation tool)
-
Configure webhooks for instant cache invalidation
-
Deploy both apps to Netlify
This project is for the Chimborazo Park Conservancy, a 501(c)(3) non-profit organization.