Skip to main content
seofields
docs

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.

Sanity Studio plugin setup
Document schema wiring
Frontend metadata rendering
Safe server-side AI endpoint guidance
short-ai-agent-prompt.md
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.

sanity.config.ts
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

FieldWhat it generates
titleSEO meta title
descriptionSEO meta description
focusKeywordPrimary focus keyword
keywordsKeyword suggestions
ogTitleOpen Graph title
ogDescriptionOpen Graph description
twitterTitleX/Twitter title
twitterDescriptionX/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.

Provider example
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 }.

sanity.config.ts
seofields({
  ai: {
    endpoint: '/api/seo/generate',
    industry: 'saas',
  },
})
Proxy contract
// 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
// 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
// Express
import { createExpressHandler } from 'sanity-plugin-seofields/server'

app.post('/api/seo/generate', createExpressHandler({
  provider: 'openai',
  apiKey: process.env.OPENAI_API_KEY,
}))
Node
// 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)
Fetch API
// 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.

Custom content 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).

Per-document-type content fields
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.

No API key required
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.

Licensed AI prompt expansion
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

OptionDescription
provideropenai, anthropic, groq, gemini, or ollama. Defaults to openai.
apiKeyProvider API key. Not required for ollama or proxy endpoint mode.
modelOverride the provider default model.
baseUrlOverride the provider base URL for OpenAI-compatible APIs.
endpointProxy endpoint: POST { field, content, focusKeyword } and return { result }.
temperatureSampling temperature. Defaults to 0.7.
contentRoot document field(s) used as source content. Defaults to body. Accepts a string, an array, or a per-document-type mapping.
industryPrompt 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).
testModeUse static demo outputs without any provider API call.
maxRetriesFull generation attempts before returning a result. Defaults to 2.
keepFirstOnValidationFailReturn 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?