AI Integration
Add AI generation buttons to SEO fields inside Sanity Studio. The plugin can generate metadata, social copy, focus keywords, and keyword suggestions using hosted models, local models, or your own proxy.
API key security: Sanity Studio is a client-side app — anything in sanity.config.ts, including apiKey, is bundled into JavaScript shipped to the browser and readable by anyone with Studio access, regardless of environment-variable naming. For production, use endpoint with a server-side proxy adapter instead. If apiKey is set without endpoint, the plugin logs a console warning to flag the exposure.
Internal fields: do not set _licenseKey or _projectId. The plugin forwards the root licenseKey and current Sanity project ID internally when needed.
AI agent prompt
Paste this into your coding agent
Use this when you want Cursor, Codex, Claude Code, or another AI coding agent to wire the plugin into an existing Sanity project. It tells the agent where to inspect first, what to implement, and how to keep AI provider keys server-side.
Configure AI generation for sanity-plugin-seofields safely.
Tasks:
1. Inspect sanity.config.ts, package.json, API routes, and deployment env conventions.
2. Register seofields() with an ai config.
3. Do not expose provider API keys in Sanity Studio client code.
4. Use ai.endpoint for production.
5. Implement a server-side endpoint with sanity-plugin-seofields/server.
6. Configure the provider, model, industry, content source fields, and fallback behavior.
7. Test the Studio generation button and the server endpoint.
Acceptance:
1. No provider API key is bundled into Studio code.
2. AI generation works from supported SEO fields.
3. The endpoint returns { result: string }.
4. Build and typecheck pass.Quick Start
Add an ai object to the plugin config. Once configured, supported fields show a generation button in Studio.
import { defineConfig } from 'sanity'
import seofields from 'sanity-plugin-seofields'
export default defineConfig({
// ...your Sanity config
plugins: [
seofields({
ai: {
provider: 'openai',
apiKey: process.env.SANITY_STUDIO_OPENAI_API_KEY,
industry: 'blog',
},
}),
],
})Generated Fields
| Field | What it generates |
|---|---|
| title | SEO meta title |
| description | SEO meta description |
| focusKeyword | Primary focus keyword |
| keywords | Keyword suggestions |
| ogTitle | Open Graph title |
| ogDescription | Open Graph description |
| twitterTitle | X/Twitter title |
| twitterDescription | X/Twitter description |
Provider Options
The built-in provider options are openai, anthropic, groq, gemini, and ollama. You can also use baseUrl for an OpenAI-compatible endpoint.
seofields({
ai: {
provider: 'groq',
apiKey: process.env.SANITY_STUDIO_GROQ_API_KEY,
model: 'llama-3.3-70b-versatile',
temperature: 0.7,
},
})Proxy Endpoint Mode
Use endpoint when you want provider keys to stay server-side. The Studio posts the field name, extracted content, focus keyword, keywords, and existing meta to your endpoint. Your endpoint returns { result: string }.
seofields({
ai: {
endpoint: '/api/seo/generate',
industry: 'saas',
},
})// Request body sent by Studio:
{
"field": "description",
"content": "Page content extracted from the document",
"focusKeyword": "sanity seo",
"keywords": ["sanity", "seo", "plugin"],
"meta": { "title": "Existing title", "description": "Existing description" }
}
// Expected response:
{
"result": "Generated SEO text"
}Server-Side Proxy Adapters
sanity-plugin-seofields/server ships ready-made proxy handlers so you don't have to write the endpoint contract yourself. Each adapter wraps the same generation + refinement pipeline used in direct-provider mode — the real apiKey stays in your server environment only.
// Next.js — app/api/seo/generate/route.ts
import { createNextRouteHandler } from 'sanity-plugin-seofields/server'
export const { POST, OPTIONS } = createNextRouteHandler({
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY,
})// Express
import { createExpressHandler } from 'sanity-plugin-seofields/server'
app.post('/api/seo/generate', createExpressHandler({
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY,
}))// Node (no framework)
import { createNodeHandler } from 'sanity-plugin-seofields/server'
import http from 'node:http'
const handler = createNodeHandler({ provider: 'openai', apiKey: process.env.OPENAI_API_KEY })
http.createServer(handler).listen(3000)// Any Fetch API runtime (Cloudflare Workers, Deno, Bun, etc.)
import { createFetchHandler } from 'sanity-plugin-seofields/server'
const handler = createFetchHandler({ provider: 'openai', apiKey: process.env.OPENAI_API_KEY })Content Source
By default, AI generation reads the document's body field. Use content to read a different field or multiple fields.
seofields({
ai: {
provider: 'openai',
apiKey: process.env.SANITY_STUDIO_OPENAI_API_KEY,
content: ['title', 'excerpt', 'body'],
},
})If different document types keep their body content in different fields, pass a per-type mapping instead. Use a default key for any type not explicitly listed (falls back to body if omitted).
seofields({
ai: {
provider: 'openai',
apiKey: process.env.SANITY_STUDIO_OPENAI_API_KEY,
content: {
page: ['sections'],
news: 'content',
work: ['content', 'contentExtended'],
author: 'bio',
default: 'body',
},
},
})Test Mode
Use testMode to preview the Studio UI without an API key or network call. Test mode returns static prewritten outputs.
seofields({
ai: {
testMode: true,
industry: 'education',
},
})Free vs Pro Prompt Banks
Every generated field has 10 prompt angle variations. How many are free depends on the industry tier — gating is per-industry, not per-field.
Free industries (8) — 4/10 free
blog, restaurant, travel, ecommerce, education, fitness, hospitality, nonprofit. 4 of the 10 prompts are free; the remaining 6 unlock with a license.
Pro industries (9) — 0/10 free
healthcare, pharmacy, finance, realestate, saas, legal, insurance, automotive, homeServices. All 10 prompts require a license — none are free.
With a valid root-level licenseKey, the public package asks seofields-pro to return the full 10-prompt pool after validating the license key and Sanity project ID. Without industry set, generation uses generic prompts, which are always free.
seofields({
licenseKey: 'SEOF-XXXX-XXXX-XXXX',
ai: {
provider: 'openai',
apiKey: process.env.SANITY_STUDIO_OPENAI_API_KEY,
industry: 'finance', // pro industry — needs licenseKey
},
})Config Reference
| Option | Description |
|---|---|
| provider | openai, anthropic, groq, gemini, or ollama. Defaults to openai. |
| apiKey | Provider API key. Not required for ollama or proxy endpoint mode. |
| model | Override the provider default model. |
| baseUrl | Override the provider base URL for OpenAI-compatible APIs. |
| endpoint | Proxy endpoint: POST { field, content, focusKeyword } and return { result }. |
| temperature | Sampling temperature. Defaults to 0.7. |
| content | Root document field(s) used as source content. Defaults to body. Accepts a string, an array, or a per-document-type mapping. |
| industry | Prompt context — 8 free industries (blog, restaurant, travel, ecommerce, education, fitness, hospitality, nonprofit) and 9 pro industries (healthcare, pharmacy, finance, realestate, saas, legal, insurance, automotive, homeServices). |
| testMode | Use static demo outputs without any provider API call. |
| maxRetries | Full generation attempts before returning a result. Defaults to 2. |
| keepFirstOnValidationFail | Return the first raw response when every validation attempt fails. |
| buttonWidth | 'full' (default) stretches the Generate button; 'auto' left-aligns it at normal width. |
Validation and Refinement
Generation is followed by targeted checks. Titles, descriptions, Open Graph, and X Card text are checked against field length rules. Title/description outputs can be refined to include required keywords. Descriptions can be simplified for readability. Focus keywords are checked against existing title and description text when available.
Was this page helpful?