Next.js 16 + React 19 + TinaCMS + Vercel
Opinionated for: Next.js App Router + TinaCMS + Tina Cloud + Vercel + Tailwind CSS 4 + shadcn/ui.
Two workflows: New Project (scaffold from scratch) or Add CMS (integrate into existing Next.js 15/16 project).
Stack
| Technology |
Version |
Key Feature |
| Next.js |
16.x |
Turbopack default, "use cache", proxy.ts, async params |
| TinaCMS |
3.x |
Git-backed CMS, visual click-to-edit, GraphQL schema API, ESM-only |
| React |
19.x |
Server Components, Actions, use(), useEffectEvent |
| Tailwind CSS |
4.x |
CSS-first config (@import "tailwindcss"), no JS config needed |
| shadcn/ui |
CLI v4 |
Copy-paste components, npx shadcn create, tw-animate-css |
| Vercel |
-- |
Deploy target. @vercel/analytics, @vercel/speed-insights, Deploy Hooks |
| Tina Cloud |
-- |
Default CMS backend. Free tier (2 users), editorial workflow on Team Plus+ |
Quick Start
npx create-next-app@latest my-site --typescript --tailwind --app --src-dir
cd my-site
npx @tinacms/cli@latest init
Verify: tinacms dev then open http://localhost:3000/admin/index.html.
Local dev runs in local-only mode -- content reads/writes directly to the filesystem, no Tina Cloud connection needed. Production builds connect to Tina Cloud via NEXT_PUBLIC_TINA_CLIENT_ID and TINA_TOKEN env vars on Vercel.
Critical Knowledge
- Server-Client split is mandatory.
useTina() requires "use client". Every editable page needs a Server Component (data fetcher) + Client Component (visual editing wrapper).
- Build order matters. Always
"build": "tinacms build && next build". Wrong order breaks with Cannot find module '../tina/__generated__/client'.
- Pin exact TinaCMS versions. No caret ranges. UI assets drift from CDN. Keep
tinacms and @tinacms/* synced via package grouping (RenovateBot or Dependabot).
tina/__generated__/ should be gitignored. Only tina-lock.json must be committed. The __generated__/ folder is rebuilt by tinacms build.
- TinaCMS 3.x is ESM-only. Config files must use
import syntax. Set "type": "module" in package.json or use .ts extensions.
- Async params in Next.js 16. Every
page.tsx, layout.tsx, route.ts must await params -- sync access fully removed.
proxy.ts replaces middleware.ts. Lives at project root, exports proxy function, runs Node.js runtime only. Codemod: npx @next/codemod@canary middleware-to-proxy .
- Security: @tinacms/cli >= 2.1.8 required. CVE-2026-28792 (Critical, CVSS 9.6) -- drive-by attacks via CORS + path traversal in older versions.
- Dev command is
tinacms dev -c "next dev". Never next dev alone -- the local GraphQL server won't run.
Version Floors
Look up current stable versions before scaffolding (npm view <pkg> version).
| Package |
Minimum |
Why |
next |
>= 16.0.10 |
Security patches |
tinacms |
>= 3.3.x |
Visual selector, ESM-only, current schema API |
@tinacms/cli |
>= 2.1.8 |
CVE-2026-28792 fix |
react / react-dom |
>= 19.0.x |
Server Components, Actions (19.2.x for useEffectEvent) |
tailwindcss |
>= 4.x |
CSS-first config (v3.4.x also acceptable) |
| Node.js |
>= 20.9.0 |
Next.js 16 requirement |
Key Imports
| Import |
From |
Used For |
defineConfig |
tinacms |
Schema configuration in tina/config.ts |
useTina |
tinacms/dist/react |
Live editing in Client Components |
tinaField |
tinacms/dist/react |
Click-to-edit data attributes |
TinaMarkdown |
tinacms/dist/rich-text |
Rich text rendering |
client |
@/tina/__generated__/client |
GraphQL queries (auto-generated) |
Collections
Add based on project needs:
| Collection |
Type |
When Needed |
pages |
Folder + blocks |
Always |
global |
Singleton |
Always (site settings, SEO defaults) |
navigation |
Singleton |
Always (can include footer links) |
posts |
Folder |
If site has a blog |
authors |
Folder |
If site has a blog |
footer |
Singleton |
If footer warrants its own collection |
notFound |
Singleton |
Optional |
Common Tasks
Create a new block
Add a template object to the blocks field's templates array in tina/config.ts. Every block needs ui.defaultItem. Optional: ui.previewSrc for visual selector thumbnails.
Add visual editing to a page
- Server Component fetches
{ data, query, variables } from Tina client
- Client Component calls
useTina() with those props
- Add
data-tina-field={tinaField(data.page, 'fieldName')} on DOM elements
Enable caching
Set cacheComponents: true in next.config.ts, then use "use cache" directive with cacheLife() presets: seconds, minutes, hours, days, weeks, max.
Deploy to Vercel
- Build command:
tinacms build && next build
- Set Vercel env vars:
NEXT_PUBLIC_TINA_CLIENT_ID, TINA_TOKEN, NEXT_PUBLIC_TINA_BRANCH
- Install
@vercel/analytics + @vercel/speed-insights, render <Analytics /> and <SpeedInsights /> in root layout
- Set up a Deploy Hook for content change rebuilds
- Use Vercel Team Environment Variables to share keys across preview/production
Always use Tina Cloud unless the project has a specific reason to self-host. See references/deployment.md for self-hosted as a secondary option.
Claude Code Automations
Set up in .claude/settings.json early in Day 0:
{
"hooks": {
"PreToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "prompt",
"prompt": "If the file path contains 'tina/__generated__/' or ends with 'tina-lock.json', BLOCK this edit. These files are auto-generated by tinacms build and must not be manually edited."
}]
}],
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "npx prettier --write $CLAUDE_FILE_PATH 2>/dev/null || true"
}]
}]
},
"permissions": {
"allow": [
"Bash(npx tinacms dev:*)",
"Bash(npx tinacms build:*)",
"Bash(pnpm dev:*)",
"Bash(pnpm build:*)",
"Bash(npx shadcn@latest add:*)"
]
}
}
MCP servers:
claude mcp add context7 -- npx -y @upstash/context7-mcp@latest -- live TinaCMS/Next.js/React/Tailwind docs
claude mcp add playwright -- npx -y @anthropic-ai/mcp-server-playwright@latest -- test visual editing in browser
Consider creating project-level skills for repeated tasks:
/new-block -- scaffold a block template + component + renderer mapping
/tinacms-check -- audit schema for missing ui.itemProps, broken tinaField, version drift
Agentic Workflow & Vibe Coding
- Iterative Building: Do not expect a perfect CMS integration on the first try. Draft the schema and visual editing wrapper, test the connection locally (
tinacms dev), isolate any specific data-fetching or rendering error, adjust exactly ONE field or query at a time, and refresh until the click-to-edit experience is seamless.
- Vibe Coding: Commit your working Next.js pages and Tina schema configurations locally before attempting complex block template additions or deploying to Vercel. Ensure
tina-lock.json is included in these commits.
Reference Files
| File |
When To Read |
references/schema-design.md |
Designing collections, blocks, fields, content hooks |
references/visual-editing.md |
Server/client split, draft mode, click-to-edit debugging |
references/seo-meta.md |
Metadata, JSON-LD, OG tags, sitemaps, RSS |
references/deployment.md |
Tina Cloud vs self-hosted, media providers, env vars |
references/setup-checklist.md |
Phased implementation checklist (MVP / Polish / Comprehensive) |
templates/tina-config-starter.ts |
Production tina/config.ts with 5 block templates |
templates/page-server-client.tsx |
Server/client page pattern with metadata and static params |
1---2name: nextjs-tinacms3description: Build Next.js 16 + React 19 + TinaCMS sites with visual editing, blocks-based page builder, and complete SEO. Use this skill whenever the user mentions TinaCMS, Tina CMS, Next.js with a CMS, visual editing with Next.js, click-to-edit, content-managed Next.js site, blocks pattern page builder, or migrating to Next.js + TinaCMS. Also trigger for TinaCMS schema design, self-hosted TinaCMS, TinaCMS media configuration, or any TinaCMS troubleshooting. Covers Day 0-2 setup from scaffolding through production deployment on Vercel.4---56# Next.js 16 + React 19 + TinaCMS + Vercel78Opinionated for: **Next.js App Router + TinaCMS + Tina Cloud + Vercel + Tailwind CSS 4 + shadcn/ui**.910Two workflows: **New Project** (scaffold from scratch) or **Add CMS** (integrate into existing Next.js 15/16 project).1112## Stack1314| Technology | Version | Key Feature |15|-----------|---------|-------------|16| Next.js | 16.x | Turbopack default, `"use cache"`, `proxy.ts`, async params |17| TinaCMS | 3.x | Git-backed CMS, visual click-to-edit, GraphQL schema API, ESM-only |18| React | 19.x | Server Components, Actions, `use()`, `useEffectEvent` |19| Tailwind CSS | 4.x | CSS-first config (`@import "tailwindcss"`), no JS config needed |20| shadcn/ui | CLI v4 | Copy-paste components, `npx shadcn create`, `tw-animate-css` |21| Vercel | -- | Deploy target. `@vercel/analytics`, `@vercel/speed-insights`, Deploy Hooks |22| Tina Cloud | -- | Default CMS backend. Free tier (2 users), editorial workflow on Team Plus+ |2324## Quick Start2526```bash27npx create-next-app@latest my-site --typescript --tailwind --app --src-dir28cd my-site29npx @tinacms/cli@latest init30```3132Verify: `tinacms dev` then open `http://localhost:3000/admin/index.html`.3334**Local dev runs in local-only mode** -- content reads/writes directly to the filesystem, no Tina Cloud connection needed. Production builds connect to Tina Cloud via `NEXT_PUBLIC_TINA_CLIENT_ID` and `TINA_TOKEN` env vars on Vercel.3536## Critical Knowledge37381. **Server-Client split is mandatory.** `useTina()` requires `"use client"`. Every editable page needs a Server Component (data fetcher) + Client Component (visual editing wrapper).392. **Build order matters.** Always `"build": "tinacms build && next build"`. Wrong order breaks with `Cannot find module '../tina/__generated__/client'`.403. **Pin exact TinaCMS versions.** No caret ranges. UI assets drift from CDN. Keep `tinacms` and `@tinacms/*` synced via package grouping (RenovateBot or Dependabot).414. **`tina/__generated__/` should be gitignored.** Only `tina-lock.json` must be committed. The `__generated__/` folder is rebuilt by `tinacms build`.425. **TinaCMS 3.x is ESM-only.** Config files must use `import` syntax. Set `"type": "module"` in `package.json` or use `.ts` extensions.436. **Async params in Next.js 16.** Every `page.tsx`, `layout.tsx`, `route.ts` must `await params` -- sync access fully removed.447. **`proxy.ts` replaces `middleware.ts`.** Lives at project root, exports `proxy` function, runs Node.js runtime only. Codemod: `npx @next/codemod@canary middleware-to-proxy .`458. **Security: @tinacms/cli >= 2.1.8 required.** CVE-2026-28792 (Critical, CVSS 9.6) -- drive-by attacks via CORS + path traversal in older versions.469. **Dev command is `tinacms dev -c "next dev"`.** Never `next dev` alone -- the local GraphQL server won't run.4748## Version Floors4950Look up current stable versions before scaffolding (`npm view <pkg> version`).5152| Package | Minimum | Why |53|---------|---------|-----|54| `next` | >= 16.0.10 | Security patches |55| `tinacms` | >= 3.3.x | Visual selector, ESM-only, current schema API |56| `@tinacms/cli` | >= 2.1.8 | CVE-2026-28792 fix |57| `react` / `react-dom` | >= 19.0.x | Server Components, Actions (19.2.x for `useEffectEvent`) |58| `tailwindcss` | >= 4.x | CSS-first config (v3.4.x also acceptable) |59| Node.js | >= 20.9.0 | Next.js 16 requirement |6061## Key Imports6263| Import | From | Used For |64|--------|------|----------|65| `defineConfig` | `tinacms` | Schema configuration in `tina/config.ts` |66| `useTina` | `tinacms/dist/react` | Live editing in Client Components |67| `tinaField` | `tinacms/dist/react` | Click-to-edit data attributes |68| `TinaMarkdown` | `tinacms/dist/rich-text` | Rich text rendering |69| `client` | `@/tina/__generated__/client` | GraphQL queries (auto-generated) |7071## Collections7273Add based on project needs:7475| Collection | Type | When Needed |76|-----------|------|-------------|77| `pages` | Folder + blocks | Always |78| `global` | Singleton | Always (site settings, SEO defaults) |79| `navigation` | Singleton | Always (can include footer links) |80| `posts` | Folder | If site has a blog |81| `authors` | Folder | If site has a blog |82| `footer` | Singleton | If footer warrants its own collection |83| `notFound` | Singleton | Optional |8485## Common Tasks8687### Create a new block8889Add a template object to the blocks field's `templates` array in `tina/config.ts`. Every block needs `ui.defaultItem`. Optional: `ui.previewSrc` for visual selector thumbnails.9091### Add visual editing to a page92931. Server Component fetches `{ data, query, variables }` from Tina client942. Client Component calls `useTina()` with those props953. Add `data-tina-field={tinaField(data.page, 'fieldName')}` on DOM elements9697### Enable caching9899Set `cacheComponents: true` in `next.config.ts`, then use `"use cache"` directive with `cacheLife()` presets: `seconds`, `minutes`, `hours`, `days`, `weeks`, `max`.100101### Deploy to Vercel1021031. Build command: `tinacms build && next build`1042. Set Vercel env vars: `NEXT_PUBLIC_TINA_CLIENT_ID`, `TINA_TOKEN`, `NEXT_PUBLIC_TINA_BRANCH`1053. Install `@vercel/analytics` + `@vercel/speed-insights`, render `<Analytics />` and `<SpeedInsights />` in root layout1064. Set up a Deploy Hook for content change rebuilds1075. Use Vercel Team Environment Variables to share keys across preview/production108109**Always use Tina Cloud** unless the project has a specific reason to self-host. See `references/deployment.md` for self-hosted as a secondary option.110111## Claude Code Automations112113Set up in `.claude/settings.json` early in Day 0:114115```json116{117 "hooks": {118 "PreToolUse": [{119 "matcher": "Edit|Write",120 "hooks": [{121 "type": "prompt",122 "prompt": "If the file path contains 'tina/__generated__/' or ends with 'tina-lock.json', BLOCK this edit. These files are auto-generated by tinacms build and must not be manually edited."123 }]124 }],125 "PostToolUse": [{126 "matcher": "Edit|Write",127 "hooks": [{128 "type": "command",129 "command": "npx prettier --write $CLAUDE_FILE_PATH 2>/dev/null || true"130 }]131 }]132 },133 "permissions": {134 "allow": [135 "Bash(npx tinacms dev:*)",136 "Bash(npx tinacms build:*)",137 "Bash(pnpm dev:*)",138 "Bash(pnpm build:*)",139 "Bash(npx shadcn@latest add:*)"140 ]141 }142}143```144145**MCP servers:**146- `claude mcp add context7 -- npx -y @upstash/context7-mcp@latest` -- live TinaCMS/Next.js/React/Tailwind docs147- `claude mcp add playwright -- npx -y @anthropic-ai/mcp-server-playwright@latest` -- test visual editing in browser148149**Consider creating project-level skills for repeated tasks:**150- `/new-block` -- scaffold a block template + component + renderer mapping151- `/tinacms-check` -- audit schema for missing `ui.itemProps`, broken `tinaField`, version drift152153## Agentic Workflow & Vibe Coding154155- **Iterative Building:** Do not expect a perfect CMS integration on the first try. Draft the schema and visual editing wrapper, test the connection locally (`tinacms dev`), isolate any specific data-fetching or rendering error, adjust exactly ONE field or query at a time, and refresh until the click-to-edit experience is seamless.156- **Vibe Coding:** Commit your working Next.js pages and Tina schema configurations locally before attempting complex block template additions or deploying to Vercel. Ensure `tina-lock.json` is included in these commits.157158## Reference Files159160| File | When To Read |161|------|-------------|162| `references/schema-design.md` | Designing collections, blocks, fields, content hooks |163| `references/visual-editing.md` | Server/client split, draft mode, click-to-edit debugging |164| `references/seo-meta.md` | Metadata, JSON-LD, OG tags, sitemaps, RSS |165| `references/deployment.md` | Tina Cloud vs self-hosted, media providers, env vars |166| `references/setup-checklist.md` | Phased implementation checklist (MVP / Polish / Comprehensive) |167| `templates/tina-config-starter.ts` | Production `tina/config.ts` with 5 block templates |168| `templates/page-server-client.tsx` | Server/client page pattern with metadata and static params |