with one click
build-tinacms-nextjs
// Use skill if you are building a Next.js App Router site with TinaCMS — git-backed MDX content, schema-driven collections, visual editing, and dynamic page rendering.
// Use skill if you are building a Next.js App Router site with TinaCMS — git-backed MDX content, schema-driven collections, visual editing, and dynamic page rendering.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | build-tinacms-nextjs |
| description | Use skill if you are building or extending a TinaCMS-backed Next.js App Router site with tina/config.ts, MDX/git-backed content, schema modeling, useTina visual editing, or TinaCloud/self-hosted deploys. |
Build, extend, and debug a Next.js App Router site whose content layer is TinaCMS (git-backed MDX/JSON, GraphQL on top, optional visual editing). This file is the router: detect the project shape, pick the lane, jump to the smallest reference set, deliver the change, verify it.
posts / pages / blocks collection in tina/config.ts."rich-text body via useTina and <TinaMarkdown> in app/[slug]/page.tsx."data-tina-field, proxy.ts, or live preview."tinacms build failures, missing generated client, schema/MDX errors, stale Vercel cache, or admin auth."Do NOT use this skill for:
tina/, no tinacms in package.json).convert-url-to-nextjs.Before drafting any code, run these scans against the user's project. Strong signals mean the skill is correctly engaged:
| Signal | Meaning |
|---|---|
tina/config.ts or tina/config.tsx | TinaCMS project — required |
tina/tina-lock.json | Schema lockfile — must be committed |
tina/__generated__/ or .tina/__generated__/ | Generated client — must be gitignored |
tinacms, @tinacms/cli in package.json | TinaCMS install |
tinacms dev -c "next dev" in scripts | App Router + Tina dev script |
import { useTina } from "tinacms/dist/react" | Visual-editing hook — Client Component only |
import { client } from "@/tina/__generated__/client" | Generated GraphQL client |
app/api/tina/[...routes]/route.ts | Self-hosted backend route |
NEXT_PUBLIC_TINA_CLIENT_ID, TINA_TOKEN, NEXT_PUBLIC_TINA_BRANCH | TinaCloud env |
proxy.ts (Next 16+) or middleware.ts | May block /admin, /api/tina/* — inspect |
Optional read-only helpers (do not modify the project):
bash scripts/check-tina-versions.sh /path/to/project — package versions, package manager, App/Pages Router shape, scripts, routes. See scripts/check-tina-versions.md.bash scripts/check-tina-env.sh /path/to/project — env names, config files, generated client, preview/admin/API files, likely lane. See scripts/check-tina-env.md.Read first: references/setup/01-prerequisites.md, references/setup/04-package-scripts.md, references/setup/05-env-vars.md, references/cli/01-overview.md, references/setup/07-agent-automation.md.
These rules cause the most expensive failures. Honor them before chasing framework bugs.
useTina() is a Client hook. The page (Server Component) fetches { data, query, variables } from the generated client; a "use client" child receives those three props and calls useTina(props). See references/rendering/01-app-router-pattern.md, references/rendering/03-usetina-hook.md.data-tina-field lands on DOM elements, not React component wrappers. Custom components must forward the attribute to a real DOM node, or the visual-editing overlay misses.tinacms build precedes next build. CI runs tinacms build && next build. Never ship a tinacms dev artifact. See references/cli/03-tinacms-build.md.tinacms, @tinacms/cli, and all @tinacms/* to the same version and update them together.tina/tina-lock.json. Never commit tina/__generated__/. See references/setup/06-gitignore-and-lockfile.md.tina/config.*. Tina builds the config in a separate Node bundle; UI imports break the build.children (in wrong context), mark, _template, _sys, id, __typename. See references/field-types/11-reserved-names.md, references/schema/03-naming-rules.md.fields: for one document shape; templates: only when documents genuinely have multiple shapes. See references/schema/02-collection-templates.md.fetchOptions: { next: { revalidate: N } } on every Tina query, or content goes stale forever. See references/rendering/11-vercel-cache-caveat.md, references/data-fetching/04-fetch-options-revalidate.md.params, searchParams, draftMode(), cookies(), headers() are async. Await them. Never use them inside a "use cache" scope.proxy.ts / middleware.ts rewrites that touch /admin, /api/preview, or /api/tina/*. See references/visual-editing/08-proxy-ts.md.example.com/blog/admin.tina/__generated__/* or .tina/__generated__/* by hand..env / host env, not .env.local. Build env vars must exist where the build actually runs.--no-optional / --omit=optional when debugging Tina module resolution.| Lane | Choose when | First references | Success check |
|---|---|---|---|
| TinaCloud | Managed auth/API, fastest path, GitHub integration, greenfield default | references/concepts/03-tinacloud-vs-self-hosted.md, references/tinacloud/01-overview.md, references/deployment/01-vercel-tinacloud.md | Admin loads, edits commit to GitHub, queries refresh via revalidate/deploy hook |
| Self-hosted | Custom auth, custom storage, private network, enterprise control, no TinaCloud dependency | references/self-hosted/00-overview.md, references/self-hosted/01-architecture.md, references/deployment/02-vercel-self-hosted.md | /api/tina/gql responds on Node runtime, auth gates writes, DB + git provider configured |
| Unknown | User has not specified | Default to TinaCloud for greenfield unless compliance / auth / storage / network constraints point to self-hosted | State the assumption before scaffolding |
Do not split this skill into Cloud and self-hosted variants. One spine; two lanes.
For visual-editing or editorial-preview tasks, verify all of these before chasing framework bugs:
NEXT_PUBLIC_TINA_CLIENT_ID, TINA_TOKEN, branch env, and any self-hosted auth/storage env vars exist in the right environment.tina/config.* declares ui.router or ui.previewUrl for collections that need live URLs.draftMode().useTina(props).query, variables, and data flow to the Client Component.data-tina-field lands on DOM elements (or is forwarded by custom components).proxy.ts (Next 16) or middleware.ts does not redirect /admin, /api/preview, or /api/tina/*.Debugging should take at most two hops from this file to the specific troubleshooting document.
pnpm. Respect the project's existing package manager when its lockfile is consistent.tinacms dev -c "next dev". Production build: tinacms build && next build.fetchOptions: { next: { revalidate: N } } for Tina client queries on Vercel unless the route is intentionally static.ui.router for collections that need live preview.defaultItem for block templates and ui.itemProps for list fields editors will manipulate.For greenfield / build tasks, deliver:
tinacms build, typecheck/build, local dev when feasible.For migration / add-to-existing tasks, deliver:
For deployment tasks, deliver:
For debugging tasks, deliver:
python3 scripts/validate-skills.py; report only target-skill errors.tina/config.*, env names, .gitignore, tina/tina-lock.json.tinacms audit where relevant, then tinacms build; verify tina/__generated__/client.* exists.tinacms build precedes next build.tinacms dev -c "next dev", open /admin/index.html, render at least one App Router route.useTina updates, click data-tina-field targets, verify proxy.ts/middleware does not block admin or Tina APIs.Use these globs as the catalog. Load only the lane the task needs. Topic catalog: references/00-reference-map.md.