← VercelCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
changed
Update to Vercel
Snapshot Sep 30, 2026 · 23:18 UTC · version 0.21.4
Collection source: not recorded for this historical snapshot. These snapshots do not have a confirmed matching collection source. Differences in file lists alone do not establish changes to the package.
Supporting file metadata differs
Newly listed paths: agents/openai.yaml. This compares saved file lists, not package contents; a different collection source can change the list.
Observed in package metadata. These changes alone do not establish a new customer-facing feature.
Supporting files
Before
[]
After
[{"relative_path":"agents/openai.yaml","size_in_bytes":169}]
Compare saved observations
Download comparison JSONFull technical diff · 1 changed fields
changed /included_files
BEFORE
[]
AFTER
[
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 169
}
]Full snapshot data
{
"name": "cms",
"description": "Headless CMS integration guidance — Sanity (native Vercel Marketplace), Contentful, DatoCMS, Storyblok, and Builder.io. Covers studio setup, content modeling, preview mode, revalidation webhooks, and Visual Editing. Use when building content-driven sites with a headless CMS on Vercel.",
"included_files": [
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 169
}
],
"skill_md_contents": "---\nname: cms\ndescription: Headless CMS integration guidance — Sanity (native Vercel Marketplace), Contentful, DatoCMS, Storyblok, and Builder.io. Covers studio setup, content modeling, preview mode, revalidation webhooks, and Visual Editing. Use when building content-driven sites with a headless CMS on Vercel.\nmetadata:\n priority: 4\n docs:\n - \"https://vercel.com/docs/solutions/cms\"\n - \"https://nextjs.org/docs/app/building-your-application/data-fetching\"\n sitemap: \"https://vercel.com/sitemap/docs.xml\"\n pathPatterns:\n - 'sanity.config.*'\n - 'sanity.cli.*'\n - 'sanity/**'\n - 'studio/**'\n - 'schemas/*.ts'\n - 'schemas/*.tsx'\n - 'src/sanity/**'\n - 'lib/sanity.*'\n - 'src/lib/sanity.*'\n - 'lib/contentful.*'\n - 'src/lib/contentful.*'\n - 'app/api/draft/**'\n - 'src/app/api/draft/**'\n - 'app/api/revalidate/**'\n - 'src/app/api/revalidate/**'\n - 'app/studio/**'\n - 'src/app/studio/**'\n bashPatterns:\n - '\\bnpm\\s+(install|i|add)\\s+[^\\n]*@sanity/client\\b'\n - '\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@sanity/client\\b'\n - '\\bbun\\s+(install|i|add)\\s+[^\\n]*@sanity/client\\b'\n - '\\byarn\\s+add\\s+[^\\n]*@sanity/client\\b'\n - '\\bnpm\\s+(install|i|add)\\s+[^\\n]*\\bnext-sanity\\b'\n - '\\bpnpm\\s+(install|i|add)\\s+[^\\n]*\\bnext-sanity\\b'\n - '\\bbun\\s+(install|i|add)\\s+[^\\n]*\\bnext-sanity\\b'\n - '\\byarn\\s+add\\s+[^\\n]*\\bnext-sanity\\b'\n - '\\bnpm\\s+(install|i|add)\\s+[^\\n]*\\bcontentful\\b'\n - '\\bpnpm\\s+(install|i|add)\\s+[^\\n]*\\bcontentful\\b'\n - '\\bbun\\s+(install|i|add)\\s+[^\\n]*\\bcontentful\\b'\n - '\\byarn\\s+add\\s+[^\\n]*\\bcontentful\\b'\n - '\\bnpm\\s+(install|i|add)\\s+[^\\n]*@builder\\.io/sdk\\b'\n - '\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@builder\\.io/sdk\\b'\n - '\\bbun\\s+(install|i|add)\\s+[^\\n]*@builder\\.io/sdk\\b'\n - '\\byarn\\s+add\\s+[^\\n]*@builder\\.io/sdk\\b'\n - '\\bnpm\\s+(install|i|add)\\s+[^\\n]*@storyblok/\\b'\n - '\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@storyblok/\\b'\n - '\\bbun\\s+(install|i|add)\\s+[^\\n]*@storyblok/\\b'\n - '\\byarn\\s+add\\s+[^\\n]*@storyblok/\\b'\n - '\\bnpm\\s+(install|i|add)\\s+[^\\n]*datocms-client\\b'\n - '\\bpnpm\\s+(install|i|add)\\s+[^\\n]*datocms-client\\b'\n - '\\bbun\\s+(install|i|add)\\s+[^\\n]*datocms-client\\b'\n - '\\byarn\\s+add\\s+[^\\n]*datocms-client\\b'\n - '\\bnpx\\s+create-sanity\\b'\n - '\\bnpx\\s+sanity\\s+init\\b'\n---\n\n# Headless CMS Integration\n\nYou are an expert in integrating headless CMS platforms with Vercel-deployed applications — covering Sanity (native Vercel Marketplace), Contentful, DatoCMS, Storyblok, and Builder.io.\n\n## Sanity (Native Vercel Marketplace Integration)\n\nSanity is the primary CMS integration on the Vercel Marketplace with first-class Visual Editing support.\n\n### Install via Marketplace\n\n```bash\n# Install Sanity from Vercel Marketplace (auto-provisions env vars)\nvercel integration add sanity\n```\n\nAuto-provisioned environment variables:\n- `SANITY_PROJECT_ID` — Sanity project identifier\n- `SANITY_DATASET` — dataset name (usually `production`)\n- `SANITY_API_TOKEN` — read/write API token\n- `NEXT_PUBLIC_SANITY_PROJECT_ID` — client-side project ID\n- `NEXT_PUBLIC_SANITY_DATASET` — client-side dataset name\n\n### SDK Setup\n\n```bash\n# Install Sanity packages for Next.js\nnpm install next-sanity @sanity/client @sanity/image-url\n\n# For embedded studio (optional)\nnpm install sanity @sanity/vision\n```\n\n### Client Configuration\n\n```ts\n// lib/sanity.ts\nimport { createClient } from \"next-sanity\";\n\nexport const client = createClient({\n projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,\n dataset: process.env.NEXT_PUBLIC_SANITY_DATASET!,\n apiVersion: \"2026-03-01\",\n useCdn: true,\n});\n```\n\n### Content Schema\n\n```ts\n// schemas/post.ts\nimport { defineType, defineField } from \"sanity\";\n\nexport const post = defineType({\n name: \"post\",\n title: \"Post\",\n type: \"document\",\n fields: [\n defineField({ name: \"title\", type: \"string\" }),\n defineField({ name: \"slug\", type: \"slug\", options: { source: \"title\" } }),\n defineField({ name: \"body\", type: \"array\", of: [{ type: \"block\" }] }),\n defineField({ name: \"mainImage\", type: \"image\", options: { hotspot: true } }),\n defineField({ name: \"publishedAt\", type: \"datetime\" }),\n ],\n});\n```\n\n### Embedded Studio (App Router)\n\n```ts\n// app/studio/[[...tool]]/page.tsx\n\"use client\";\nimport { NextStudio } from \"next-sanity/studio\";\nimport config from \"@/sanity.config\";\n\nexport default function StudioPage() {\n return <NextStudio config={config} />;\n}\n```\n\n```ts\n// sanity.config.ts\nimport { defineConfig } from \"sanity\";\nimport { structureTool } from \"sanity/structure\";\nimport { visionTool } from \"@sanity/vision\";\nimport { post } from \"./schemas/post\";\n\nexport default defineConfig({\n name: \"default\",\n title: \"My Studio\",\n projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,\n dataset: process.env.NEXT_PUBLIC_SANITY_DATASET!,\n plugins: [structureTool(), visionTool()],\n schema: { types: [post] },\n});\n```\n\n### Live Content with `defineLive()` (next-sanity v12)\n\nUse `defineLive()` for automatic real-time content updates without manual revalidation. In next-sanity v11+, `defineLive` must be imported from the `next-sanity/live` subpath:\n\n```ts\n// lib/sanity.ts\nimport { createClient } from \"next-sanity\";\nimport { defineLive } from \"next-sanity/live\";\n\nconst client = createClient({\n projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,\n dataset: process.env.NEXT_PUBLIC_SANITY_DATASET!,\n apiVersion: \"2026-03-01\",\n useCdn: true,\n});\n\nexport const { sanityFetch, SanityLive } = defineLive({\n client,\n // Required for draft content in Visual Editing — use a Viewer role token\n serverToken: process.env.SANITY_API_TOKEN,\n // Optional but recommended for faster live preview\n browserToken: process.env.SANITY_BROWSER_TOKEN,\n});\n```\n\n```tsx\n// app/page.tsx\nimport { sanityFetch, SanityLive } from \"@/lib/sanity\";\n\nexport default async function Page() {\n const { data: posts } = await sanityFetch({ query: `*[_type == \"post\"]` });\n return (\n <>\n {posts.map((post) => <div key={post._id}>{post.title}</div>)}\n <SanityLive />\n </>\n );\n}\n```\n\n> **Breaking change in v12**: `defineLive({fetchOptions: {revalidate}})` has been removed. `defineLive({stega})` is deprecated.\n\n### Visual Editing (Presentation Mode)\n\nSanity Visual Editing lets content editors click-to-edit content directly on the live site preview. Requires Sanity Studio v5+ (React 19.2) and `@sanity/visual-editing` v5+.\n\n```bash\nnpm install @sanity/visual-editing\n```\n\nIn next-sanity v11+, `VisualEditing` must be imported from the `next-sanity/visual-editing` subpath:\n\n```ts\n// app/layout.tsx\nimport { VisualEditing } from \"next-sanity/visual-editing\";\nimport { draftMode } from \"next/headers\";\n\nexport default async function RootLayout({ children }: { children: React.ReactNode }) {\n const { isEnabled } = await draftMode();\n return (\n <html>\n <body>\n {children}\n {isEnabled && <VisualEditing />}\n </body>\n </html>\n );\n}\n```\n\n### On-Demand Revalidation Webhook\n\n```ts\n// app/api/revalidate/route.ts\nimport { revalidateTag } from \"next/cache\";\nimport { parseBody } from \"next-sanity/webhook\";\n\nexport async function POST(req: Request) {\n const { isValidSignature, body } = await parseBody<{\n _type: string;\n slug?: { current?: string };\n }>(req, process.env.SANITY_REVALIDATE_SECRET);\n\n if (!isValidSignature) {\n return Response.json({ message: \"Invalid signature\" }, { status: 401 });\n }\n\n if (body?._type) {\n revalidateTag(body._type);\n }\n\n return Response.json({ revalidated: true, now: Date.now() });\n}\n```\n\nConfigure the webhook in Sanity at **Settings → API → Webhooks** pointing to `https://your-site.vercel.app/api/revalidate`.\n\n## Contentful\n\n```bash\nnpm install contentful\n```\n\n```ts\n// lib/contentful.ts\nimport { createClient } from \"contentful\";\n\nexport const contentful = createClient({\n space: process.env.CONTENTFUL_SPACE_ID!,\n accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,\n});\n```\n\n### Fetching Entries\n\n```ts\n// app/page.tsx\nimport { contentful } from \"@/lib/contentful\";\n\nexport default async function Page() {\n const entries = await contentful.getEntries({ content_type: \"blogPost\" });\n return (\n <ul>\n {entries.items.map((entry) => (\n <li key={entry.sys.id}>{entry.fields.title as string}</li>\n ))}\n </ul>\n );\n}\n```\n\n## Draft Mode (Preview)\n\nAll CMS integrations should use Next.js Draft Mode for preview:\n\n```ts\n// app/api/draft/route.ts\nimport { draftMode } from \"next/headers\";\n\nexport async function GET(req: Request) {\n const { searchParams } = new URL(req.url);\n const secret = searchParams.get(\"secret\");\n\n if (secret !== process.env.DRAFT_SECRET) {\n return Response.json({ message: \"Invalid token\" }, { status: 401 });\n }\n\n const draft = await draftMode();\n draft.enable();\n\n const slug = searchParams.get(\"slug\") ?? \"/\";\n return Response.redirect(new URL(slug, req.url));\n}\n```\n\n## Environment Variables\n\n| Variable | Scope | CMS | Description |\n|----------|-------|-----|-------------|\n| `SANITY_PROJECT_ID` / `NEXT_PUBLIC_SANITY_PROJECT_ID` | Server / Client | Sanity | Project identifier |\n| `SANITY_DATASET` / `NEXT_PUBLIC_SANITY_DATASET` | Server / Client | Sanity | Dataset name |\n| `SANITY_API_TOKEN` | Server | Sanity | Read/write token |\n| `SANITY_REVALIDATE_SECRET` | Server | Sanity | Webhook secret for revalidation |\n| `CONTENTFUL_SPACE_ID` | Server | Contentful | Space identifier |\n| `CONTENTFUL_ACCESS_TOKEN` | Server | Contentful | Delivery API token |\n| `CONTENTFUL_PREVIEW_TOKEN` | Server | Contentful | Preview API token |\n| `DATOCMS_API_TOKEN` | Server | DatoCMS | Read-only API token |\n\n## Cross-References\n\n- **Marketplace install and env var provisioning** → `⤳ skill: marketplace`\n- **On-demand revalidation and caching** → `⤳ skill: runtime-cache`\n- **Draft mode and middleware patterns** → `⤳ skill: routing-middleware`\n- **Environment variable management** → `⤳ skill: env-vars`\n- **Image optimization** → `⤳ skill: nextjs`\n\n## Official Documentation\n\n- [Sanity + Vercel Marketplace](https://vercel.com/marketplace/sanity)\n- [next-sanity Documentation](https://github.com/sanity-io/next-sanity) (v12)\n- [next-sanity v11→v12 Migration](https://github.com/sanity-io/next-sanity/releases)\n- [Sanity Visual Editing](https://www.sanity.io/docs/visual-editing)\n- [Contentful JavaScript SDK](https://www.contentful.com/developers/docs/javascript/)\n- [Next.js Draft Mode](https://nextjs.org/docs/app/building-your-application/configuring/draft-mode)\n"
}SHA-256: c49a78c743f23603182668305025ce856e10df902b6f0594579ff8d25e0581f1