Skip to main content
seofields
docs
.md

Changelog

All notable changes to sanity-plugin-seofields. Follows Keep a Changelog · Semantic Versioning

+Added~Changed×FixedRemovedSecurity!Deprecated
v1.10.0Jul 11, 2026latest+3

+ ✨ Added

+Custom AI prompts — write your own prompt wording using the same document values the built-in prompts use. A custom prompt function receives a CustomPromptValues object (field, content, focusKeyword, keywords, meta, industry) — the extracted document text and metadata — and returns the prompt string sent to the provider. See AI.md.
·Free tier — ai.customPrompt — a single custom prompt function (values) => string. The function itself branches on field/industry.
·Pro tier — ai.customPrompts{ generic?, byIndustry?, merge? } for up to 5 generic + 5 per-industry custom prompts, unlocked only behind a validated license via seofields-pro. Without a valid license this collapses to a single prompt (same as the free tier).
·Replace or merge — custom prompts replace the built-in angle pool for a field by default; set merge: true to mix them into the built-in pool instead.
·Proxy-mode note — functions can't cross the HTTP boundary, so in ai.endpoint (proxy) mode custom prompts must be set on the server handler config passed to createSeoAiHandler, not on the Studio's ai config.
+Hreflang auto-populate — derive hreflang alternates from document translations instead of typing them by hand (designed for @sanity/document-internationalization).
·buildHreflangs(translations, options) (exported from /head and /next) — maps resolved _translations ({language, slug}) into {locale, url} entries. Supports baseUrl, resolvePath, xDefault, and a current document; dedupes by locale.
·buildSeoMeta({ hreflangs }) — new option that supersedes seo.hreflangs, so alternates can come straight from buildHreflangs.
·Studio hreflang: { autoFill } config — adds a "Sync from translations" button to the hreflangs field that reads translation.metadata references and fills entries (still editable). No new dependency.
+llms.txt generatorbuildLlmsTxt(options) + docsToLlmsSection(docs, options) (exported from /head and /next) build an llmstxt.org-format file from Sanity content: H1 title, summary blockquote, and ## link sections. Serve it from a /llms.txt route or build step.
v1.9.0Jul 4, 2026+10×2

+ ✨ Added

+Framework-neutral frontend head export — new sanity-plugin-seofields/head entry point for Astro, Nuxt, Vue, SvelteKit, Remix, and custom SSR frontends. It exports buildSeoHead, buildSeoMeta, sanitizeOGType, sanitizeTwitterCard, and related types without importing the Studio plugin entry or React components.
+buildSeoHead(options) — converts seoFields data into plain serializable { title, meta, link } head data. Use it with Astro layouts, Nuxt/Vue useHead, SvelteKit <svelte:head>, Remix meta() mappings, or any custom head renderer.
+Expanded frontend integration documentation — README and website docs now include examples for Next.js App Router, Next.js Pages Router, Astro, Nuxt 3, Vue 3, and SvelteKit using the actual seoFields field names (seo.title, seo.description, seo.twitter.card, seo.hreflangs, etc.).
+Server-side AI proxy adapters — new sanity-plugin-seofields/server entry point with createSeoAiHandler plus ready-made framework adapters: createNextRouteHandler, createExpressHandler, createNodeHandler, and createFetchHandler (any Fetch API runtime — Cloudflare Workers, Deno, Bun). Each wraps the same generation + refinement pipeline used in direct-provider mode, but keeps the real apiKey server-side only. See AI.md.
+API key exposure warning — if ai.apiKey is set without ai.endpoint, the plugin now logs a console.warn in the browser explaining that Studio bundles are client-side and the key is readable by anyone with Studio access.
+Proxy mode (ai.endpoint) sends keywords, meta, industry, and license key — the request body includes field, content, focusKeyword, keywords, meta, industry, and licenseKey, so proxied requests get the same keyword-injection, refinement passes, and industry-specific prompt pool as direct-provider mode.
+17 AI industries across two tiers — free tier: blog, restaurant, travel, ecommerce, education, fitness, hospitality, nonprofit (4 of 10 prompts free per field, remaining 6 unlock with a license). Pro tier: healthcare, pharmacy, finance, realestate, saas, legal, insurance, automotive, homeServices (all 10 prompts require a license).
+10 prompt angle variations per field — each generated field (title, description, focus keyword, keywords, OG/Twitter title/description) draws from 10 prompt variations.
+AI content generation — new "Generate with AI" and keyword suggestion features across title, description, focus keyword, keywords, and OG/Twitter fields. See AI.md, which documents providers, industries/tiers, the server-side proxy adapters, content-source mapping, and the full config reference in one place.
+Real keyword-quality health scoring — the SEO Health Dashboard's keywords scoring bucket (10 points) no longer just checks that a keywords array is non-empty. It now grades whether keywords/focusKeyword are actually used in the title or description, rewards focus-keyword prominence (start of title > elsewhere in title > description only), and penalizes keyword stuffing — capped at the same 10-point budget so total scores stay on the existing 0–100 scale. A new shared getFocusKeywordPlacement helper backs both this scoring and the live focusKeyword field feedback, so the two never drift apart.

× 🐛 Fixed

×Frontend image and canonical fallbacksbuildSeoMeta() now respects seo.canonicalUrl before constructing a canonical URL from baseUrl + path, falls back to seo.metaImage when no Open Graph image is set, and exposes seo.hreflangs as alternates.languages for Next.js metadata consumers.
×hasKeywordOveruse crash on regex-metacharacter keywords — keywords containing characters like + (e.g. "C++") previously threw Invalid regular expression: Nothing to repeat when checked for stuffing, in every place that ran the check (title/description/OG/Twitter live feedback, and now health scoring). Keywords are now escaped before being used in a RegExp.
v1.8.0Jun 26, 2026+5·1~3

+ ✨ Added

+focusKeyword field — New string field inside seoFields with a live, colour-coded inline feedback component (FocusKeywordInput). As editors type their target keyword the component immediately checks:
·Title prominence — green if the keyword is at the start, orange if it appears elsewhere, red if it is missing.
·Description presence — green if present, orange if missing.
·URL slug match — green if the slug contains the keyword (only evaluated when a slug field exists on the document), orange if absent.
·Stuffing guard — red warning when the keyword appears more than twice in the title.
+hreflangs field + hreflangEntry schema type — New array field on seoFields that stores international SEO alternate entries. Each item is an hreflangEntry object with:
·locale — BCP 47 language/region tag (e.g. en, fr-FR, x-default). Validated with a regex pattern in Studio; invalid tags show an inline error before saving.
·url — Absolute URL of the alternate page. Validated as http or https.
+GEOChecklist component + geoChecklist field — GEO (Generative Engine Optimization) readiness panel rendered directly inside seoFields. Gives editors instant feedback on how well the page is structured for AI-powered overviews (Google AI Overviews, ChatGPT, Perplexity).
+MetaTagsPreview component + metaTagsPreview field — Live HTML <head> preview rendered inside seoFields. Shows the exact output that will be in the page source:
·<title>, <meta name="description">, <meta name="keywords">, <meta name="robots">
·<link rel="canonical">
·og:title, og:description, og:url, og:type, og:site_name, og:image
·twitter:card, twitter:site, twitter:creator, twitter:title, twitter:description, twitter:image
·<link rel="alternate" hreflang="..."> entries
·Custom metaAttributes key/value pairs
+Root-level licenseKey optionSeoFieldsPluginConfig.licenseKey is now the single place to supply your pro license key. It is forwarded automatically to the SEO Health Dashboard, Publish Gate, and GEO Checklist pro checks — no need to repeat it per feature.

· 🗑️ Deprecated

·healthDashboard.licenseKey — Move licenseKey to the root config instead. The old key still works (used as a fallback when the root key is absent) but logs a console warning and shows an amber banner inside the dashboard UI.

~ 🔄 Changed

~SeoObjectFieldName union expandedfocusKeyword, hreflangs, geoChecklist, and metaTagsPreview are now valid values in the SeoObjectFieldName union type, making them fully assignable in typed fieldGroups arrays.
~SeoFieldsPluginConfig — two new top-level options:
·geo?: boolean — set false to hide the GEO Checklist on all document types (default: true).
·metaTagsPreview?: boolean — set false to hide the Meta Tags Preview on all document types (default: true).
~SeoFields TypeScript typefocusKeyword?: string and hreflangs?: Array<{locale: string; url: string}> added to the exported interface.
v1.7.0Jun 11, 2026+1×1·1

+ ✨ Added

+OpinionNewsArticle Schema.org type — New schemaOrgOpinionNewsArticle type (a NewsArticle subtype per Schema.org spec) aimed at strengthening E-E-A-T signals for opinion, editorial, and column content. Ships with:
·Sanity schema (schemaOrgOpinionNewsArticle) registered through the combined schemaOrg() plugin or individually via schemaOrgOpinionNewsArticlePlugin.
·Next.js React component (OpinionNewsArticleSchema) and JSON-LD builder (buildOpinionNewsArticleJsonLd) exported from sanity-plugin-seofields/next.
·Full TypeScript types: SchemaOrgOpinionNewsArticleData, SchemaOrgOpinionNewsArticleConfig.
·Fields: headline (required), description, image, author (polymorphic Person/Organization), publisher (polymorphic Organization/Person), datePublished (required), dateModified, articleSection, articleBody, dateline, printColumn, printEdition, printPage, printSection, backstory.
·Searchable in the Type Picker dialog with keywords opinion, editorial, column, commentary, op-ed.
·DocumentTextIcon icon to match NewsArticle.
·Suggested in #12 by @WebShapedBiz. Thanks to Siniša Perović for the thoughtful publisher-focused E-E-A-T use case.

× 🐛 Fixed

×Polymorphic select fields leaking variant into JSON-LD output — When an editor selected a variant (e.g. chose "Person" for the author field) but left all sub-fields blank, the raw Sanity data object { variant: 'person' } was emitted directly into the JSON-LD output. Schema.org validators (including Google's Rich Results Test) would report: _"The property variant is not recognised by the schema (e.g. schema.org) for an object of type Thing."_ The handleSelectVariant function in generator.ts now returns true (consumed — emit nothing) instead of false when the variant's nested sub-object is null or empty, preventing the raw value from falling through to the generic field emitter. This fix applies to all polymorphic select fields across every Schema.org type (author, publisher, image, genre, keywords, etc.).

· 🧪 Tests

·4 new regression tests in src/__tests__/schemaOrg.test.ts covering the variant leak fix:
·Asserts variant never appears in JSON-LD when author variant is set but sub-object is empty.
·Asserts variant never appears in JSON-LD when publisher variant is set but sub-object is empty.
·Asserts author correctly resolves to { "@type": "Person", ... } when sub-fields are populated.
·Asserts publisher correctly resolves to { "@type": "Organization", ... } when sub-fields are populated.
v1.6.5May 14, 2026×1

× 🐛 Fixed

×Meta title suffix length feedback and SERP spacing — Fixed the meta title validator so the SEO feedback now checks the combined title + suffix length, explicitly notes when the suffix value is included in the count, and keeps the live SERP preview aligned with the same | separator spacing used in the rendered title. Also wired the meta title input to resolve configured titleSuffix / titleSuffixQuery values directly so the validation message stays accurate when suffixes are enabled. Reported in #9. Thanks to @patricksdev for the report and follow-up suggestion.
v1.6.4May 10, 2026×1

× 🐛 Fixed

×Combined schemaOrg() validation for LocalBusiness and LegalService — Fixed a schema validation bug where both types declared two polymorphic image fields with the default image name, causing sanity schema validate to fail when importing the full combined schemaOrg config. The logo field in both schemas now uses the explicit logo field name, so image and logo serialize separately and the combined export validates correctly. Reported in #8. Thanks to @ahenriksen-inferno for the clear root-cause report.
v1.6.3May 10, 2026+1~1·2

+ ✨ Added

+Legacy user upgrade banner — Users whose license key is recognised as a "legacy" (free-beta) key now see an amber banner inside the SEO Health Dashboard instead of a hard block. The banner shows the upgrade deadline date (if supplied by the server), a human-readable formatted date, a direct Upgrade for $10/project → call-to-action link, and an Already paid? Click here to verify your license → action to immediately re-check the license after purchase. The dashboard content remains fully accessible behind the banner so existing users are not disrupted.

~ 🔄 Changed

~Package metadata and peer dependencies — Added @sanity/icons as a peer dependency (>=2) and dev dependency, and refreshed the npm package description / keywords to better reflect Sanity v3/v4/v5 support, the built-in audit dashboard, and the plugin's broader SEO + Schema.org feature set.

· 📝 Documentation

·README overhaul — Renamed package heading to sanity-plugin-seofields, rewrote the tagline to lead with the audit dashboard differentiator, expanded package.json keywords (added sanity-v3/v4/v5, twitter-card, robots, json-ld, schema-org, structured-data, seo-dashboard, metadata, headless-cms, nextjs), and updated the description to reflect the full feature set.
·Docs site review link — Added a call-to-action in the README asking users to leave a review on the documentation site (sanity-plugin-seofields.thehardik.in/reviews) alongside the existing Sanity Plugin Directory and GitHub star links.
v1.6.2May 3, 2026×2

× 🐛 Fixed

×titleSuffix separator convention — The | separator between the meta title and suffix is now always added automatically by the plugin. Users should provide only the bare text (e.g. 'Acme Inc', not '| Acme Inc'). Providing a leading pipe would have resulted in a double-pipe (|| Acme Inc) in the SERP preview.
×titleSuffix character counter — The 60-character title limit now correctly accounts for the | separator (2 extra characters) added by the plugin, so the counter remains accurate when a suffix is configured.
v1.6.1Apr 25, 2026+4·1

+ ✨ Added

+seoPreview.titleSuffix — Append a static string or dynamic function-based suffix to the meta title in the Live SEO Preview (e.g. Acme Inc — the | separator is added automatically by the plugin). The character counter for the title field automatically subtracts the suffix length (plus the | separator) so the limit stays accurate. Requested in #6.
+seoPreview.titleSuffixQuery — Dynamic GROQ-based title suffix fetched at runtime from the dataset (takes priority over titleSuffix). Useful when the suffix is stored in a site-settings document.
+seoPreview.titleSuffixInheritColor — When true, the suffix in the SERP preview renders in the same blue as the main title (#1a0dab) instead of the default grey (#70757a).
+Root-level apiVersion — New top-level apiVersion option on SeoFieldsPluginConfig. Used internally by the Live Preview's GROQ client and by the SEO Health Dashboard, eliminating the need to set it in both places.

· 🗑️ Deprecated

·healthDashboard.apiVersion — Move apiVersion to the root config instead. The old key still works but logs a console warning and shows an amber banner inside the dashboard UI.
v1.6.0Apr 22, 2026+2

+ ✨ Added

+14 new Schema.org types — The combined schemaOrg array now registers 38 types (up from 24). New additions: LegalService, Country, JobPosting, Restaurant, Movie, Book, NewsArticle, ItemList, ProfilePage, MusicRecording, MusicAlbum, plus Recipe, Service, and SocialMediaPosting (previously individual-only) are now first-class combined-array members. Each type ships with a Sanity schema, Next.js JSON-LD component, buildXxxJsonLd() helper, category metadata, and icon.
+Individual plugin exports for each new type (schemaOrgLegalServicePlugin, schemaOrgCountryPlugin, schemaOrgJobPostingPlugin, etc.) alongside the default default schemaOrgXxx factory re-exports from sanity-plugin-seofields/schema.
v1.5.5Apr 15, 2026+3

+ ✨ Added

+fieldGroups config option — New top-level plugin option that groups SEO fields into tabbed sections (Sanity groups) inside the seoFields object. Editors can switch between tabs like "Meta", "Open Graph", and "Twitter Card" for a cleaner editing experience. Applies universally to every document using type: 'seoFields'.
+SeoFieldGroup type export — New TypeScript interface for defining field groups, exported from the package entry point.
+SeoObjectFieldName type export — Union type of all top-level field names in the seoFields object ('title' | 'description' | 'metaImage' | …), exported for type-safe group configuration.
v1.5.4Apr 15, 2026+2

+ ✨ Added

+baseMeta schema type — New baseMeta object type that groups the core meta fields (title, description, metaImage, keywords, canonicalUrl, metaAttributes) into a single reusable Sanity object, following the same pattern as the existing openGraph and twitter types. This allows using basic meta fields independently outside of the top-level seoFields object.
+baseMetaSchema export — The new baseMeta type is exported from the package entry point as baseMetaSchema for standalone use:
v1.5.3Apr 13, 2026~2·1

~ 🔄 Changed

~buildSeoMeta refactor — Extracted three private helper functions from buildSeoMeta in seoMeta.ts: resolveOgImage, resolveTwitterImage, and buildCustomMetaMap. Logic is unchanged; separation improves readability and testability.
~SeoMetaTags return type — Added explicit React.JSX.Element return type annotation to SeoMetaTags in SeoMetaTags.tsx.

· 🧹 Internal

·Removed unused SeoFields type import from seoMeta.ts.
v1.5.2Apr 3, 2026+4~3

+ ✨ Added

+Export to CSV / JSON — New export buttons in the dashboard controls bar let you download all currently-filtered documents as a .csv or .json file. Controlled via the new export option in healthDashboard config:
+Pagination — The documents table is now paginated. Default page size is 25 and can be switched to 50, 100, or 200 per page. The selected page size persists across sessions via localStorage. Navigation controls show the current page, total pages, and the document range being displayed.
+Compact stats mode — New compactStats option in healthDashboard config (defaults to false). When true, replaces the 6-card stats grid with a single row of inline stat pills, saving vertical space in tighter layouts.
+Persistent filters — The status and type filter dropdown selections are now saved to localStorage and restored automatically on next visit.

~ 🔄 Changed

~Extracted RenderLicenseLoading and RenderLicenseInvalid as standalone components, making the main render path significantly cleaner.
~Moved VALIDATION_ENDPOINT and CACHE_TTL_MS constants to module scope (previously defined inside the component on every render).
~Filter change handlers now reset the table back to page 1 to avoid showing an empty page when the result count changes.
v1.5.1Mar 31, 2026+1

+ ✨ Added

+Theme switcher in SEO Health Dashboard — A three-way light / dark / system toggle is now rendered in the dashboard header (sun, moon, and monitor icons). The selected theme persists across sessions via localStorage. System mode automatically follows the OS prefers-color-scheme preference and updates live when the OS preference changes.
v1.5.0Mar 29, 2026+10

+ ✨ Added

+seofields CLI — A new seofields binary is included with the package. Run commands directly from your terminal with npx seofields <command>.
+seofields create-config — Interactive wizard that scaffolds a seofields.cli.ts or seofields.cli.js config file with your project credentials. No more manual config file creation or copy-pasting required.
+seofields init — Automatically detects your sanity.config.ts/js and injects the seofields() plugin if not already registered. Supports --schema-org flag to also inject schemaOrg().
+seofields config — Update seofields() configuration options directly from the terminal using dotted key flags (e.g. --baseUrl=https://example.com, --healthDashboard.licenseKey=SEOF-…).
+seofields report — Queries your Sanity dataset and prints an SEO health report as a formatted table or summary. Scores each document across title, description, image, keywords, and Open Graph fields.
+seofields export — Exports all documents with SEO fields as JSON or CSV. Useful for audits and spreadsheet analysis.
+seofields doctor — Checks your local setup: verifies seofields() is registered in sanity.config, confirms peer dependencies are installed, and detects your CLI config file.
+seofields.cli.ts config file — Create a seofields.cli.ts (or .js) file using defineSeoCli() to store your project ID, dataset, and token so you never need to pass flags on every command.
+defineSeoCli() helper — Importable from sanity-plugin-seofields/define-cli. Provides full TypeScript types for the CLI config object including showConnectionInfo and types options.
+Random color themes — The CLI banner uses a random accent color on every run, drawn from the plugin's brand color palette.
v1.4.2Mar 28, 2026~3

~ 🔄 Changed

~Sanity v5 compatibility — Added sanity ^5 to peer dependencies. The plugin now officially supports Sanity Studio v3, v4, and v5. No API changes were required — all existing Studio APIs used by the plugin remain unchanged in v5.
~Updated devDependencies to use sanity@^5.0.0 for development and testing.
~Updated README description to reflect v3/v4/v5 support.
v1.4.1Mar 25, 2026×1

× 🐛 Fixed

×IssuesPopover positioning in desk panes — The popover that displays hidden SEO issues now renders at the correct position when using createSeoHealthPane inside a Sanity desk structure. Changed position: absolute to position: fixed so popover coordinates (from getBoundingClientRect(), which are viewport-relative) align correctly regardless of parent container context or scroll state.
v1.4.0Mar 25, 2026+8·1

+ ✨ Added

+schemaOrg() — combined Sanity plugin (all 24 types + the schemaOrg array type)
+schemaOrgArticlePlugin(), schemaOrgFAQPagePlugin(), schemaOrgProductPlugin() … (one per type)
+Individual raw schema definitions: schemaOrgArticle, schemaOrgWebsite, schemaOrgProduct … (24 total, for manual schema.types registration)
+generateSchemaType() — generator utility for building custom Schema.org types
+buildGenericJsonLd() — generic JSON-LD builder
+TypeScript interfaces: SchemaFieldDef, SchemaFieldOption, SchemaTypeDef, SchemaOrgConfig, SchemaOrgCombinedConfig
+SchemaOrgScripts — renders all JSON-LD scripts from a combined schemaOrg array field, automatically dispatching to the correct renderer per _type
+SchemaOrgScript — low-level single script tag renderer

· 🚀 Improved

·SeoHealthTool and SeoHealthDashboard are now lazy-loaded — wrapped in React.lazy() + React.Suspense. The Studio bundle no longer pays the upfront cost of loading the dashboard code until it is actually navigated to, improving initial Studio load time.
v1.3.2Mar 23, 2026+2~6

+ ✨ Added

+Refresh button — a "Refresh" button now appears in the dashboard header. Clicking it re-fetches documents without a full-page loading flash: the button icon spins while the update completes, and the table updates in place. The button is disabled while an initial load or refresh is already in progress.
+Non-string title warning — when a document's title field is not a plain string (e.g. a Portable Text array returned from a custom GROQ query), an amber inline badge is shown in the Title column instead of a broken/empty link. The badge reads *"⚠ title is not a string — use pt::text(title) in query.groq"* with a tooltip explaining the fix.

~ 🔄 Renamed (with backwards-compatible deprecations)

~display: {
~typeColumn: true,
~documentId: false,
~},
~typeLabels: { productDrug: 'Products' },
~docBadge: (doc) => ({ label: doc.status }),
v1.3.1Mar 21, 2026·2

· 🧹 Internal

·Replaced as any type casts in schema index with proper FieldDefinition typing.
·Fixed negated condition in image cross-check logic.
v1.3.0Mar 21, 2026+3·2

+ ✨ Added

+Inline image validation components — five new Sanity Studio input components that render live, colour-coded feedback hints directly below image upload/URL fields. Feedback cross-checks whether all three image types (meta, OG, Twitter) are set, prompting the editor when any are missing.
+Image validation helpers — new utility functions that power the inline components and can also be reused in custom tooling. Each function returns colour-coded feedback messages and performs cross-field image coverage checks.
+Added an isSubImageSet utility that correctly detects whether an OG or Twitter sub-object has an image configured, supporting both asset upload and URL modes.

· 🔧 Changed

·SEO Health Dashboard scoring — OpenGraph and Twitter card scores are now included in the overall score. Added an Image Completeness bonus when all three image types are present, and robots noIndex now contributes to the score when indexing is enabled.
·Image validation components are automatically registered on their respective schema fields — no consumer-side configuration needed; existing installs pick up the inline hints on upgrade.
v1.2.7Mar 19, 2026×1

× 🐛 Fixed

×SeoFieldsInput now exported from sanity-plugin-seofields/next — the type was defined in seoMeta.ts but missing from the /next re-export, making import type { SeoFieldsInput } from 'sanity-plugin-seofields/next' fail at compile time.
v1.2.6Mar 19, 2026·3

· 🔧 Internal

·Migrated build tool from @sanity/pkg-utils to tsup — replaced package.config.ts with tsup.config.ts for a leaner, faster build pipeline.
·Simplified TypeScript configuration — removed separate tsconfig.dist.json and tsconfig.settings.json, consolidating settings into a single tsconfig.json.
·Updated package.json: set "type": "module", added "main" / "module" fields, and expanded the exports map with explicit types, source, import, and require conditions for both entry points.
v1.2.5Mar 18, 2026~2

~ 🔄 Changed

~Cleaned up internal JSDoc and website documentation wording for the sanity-plugin-seofields/next entry point.
~Reverted exports map to flat string values (import/require as plain strings) — @sanity/pkg-utils does not support nested condition objects and rejects the package during build validation.
v1.2.4Mar 18, 2026+3~4

+ ✨ Added

+sanity-plugin-seofields/next entry point — New sub-path export for use in Next.js Server Components and generateMetadata() functions.
+buildSeoMeta(options) — Converts a Sanity SEO object into a structured metadata object compatible with Next.js App Router's Metadata type. Returns title, description, keywords, robots, openGraph, twitter, alternates.canonical, and other (custom meta attributes). Safe to call in RSC / generateMetadata().
+<SeoMetaTags> — Framework-agnostic React component that renders all SEO meta tags as plain React elements (<title>, <meta>, <link rel="canonical">). Designed for Next.js Pages Router <Head>, Nuxt, Remix, or any SSR <head> slot. Also works in Next.js App Router RSC when imported from sanity-plugin-seofields/next.

~ 🔄 Changed

~MetaAttribute.key and MetaAttribute.type are now optional (key?, type?) — previously required, this change aligns the type with real Sanity document shapes where these fields may be absent.
~OpenGraphSettings gains an explicit url?: string field (maps to og:url).
~TwitterCardSettings gains an explicit creator?: string field (maps to twitter:creator).
~SeoFields now includes metaAttributes?: MetaAttribute[] in its type definition (was missing despite being part of the schema).
v1.2.3Mar 12, 2026+1

+ ✨ Added

+structureTool option — New healthDashboard.structureTool option that routes document-title clicks directly to a named Structure tool instead of relying on Sanity's generic intent resolver. Required when you have multiple structure tools and the monitored documents live in a non-default one. Clicking a title navigates to /{basePath}/{structureTool}/intent/edit/id=…;type=…/ directly.
v1.2.2Mar 11, 2026+3

+ ✨ Added

+Desk Structure Pane (createSeoHealthPane) — Embed the SEO Health Dashboard directly inside Sanity Studio's Structure tool.
·createSeoHealthPane(S, options) — returns a ComponentBuilder with a built-in .child() resolver so clicking any document row opens the document editor as a pane to the right. Automatically wires up schemaType via childParameters.
·options.licenseKey is required.
·Exported from the package root as createSeoHealthPane and SeoHealthPaneOptions.
+Title truncation in split pane — Document titles now truncate with an ellipsis at any pane width. Fixed by propagating min-width: 0 through the full flex ancestor chain and adding a dedicated TitleCell wrapper.
+Stats grid auto-wrapping — The stats bar cards now use repeat(auto-fit, minmax(130px, 1fr)) instead of a fixed 6-column grid. Cards wrap naturally at narrow pane widths and always fill the full available width when there is space.
v1.2.1Mar 10, 2026+1~1

+ ✨ Added

+previewMode — New healthDashboard.previewMode option that loads realistic dummy documents into the SEO Health Dashboard without querying your dataset. Useful for showcasing, documentation screenshots, and testing the dashboard UI before real content exists.
·License validation is bypassed when previewMode is active.
·An amber "Preview Mode" badge is shown in the dashboard header to make the state visually obvious.

~ 🔄 Changed

~Dynamic type-badge colours — Type badges in the Type column are no longer mapped to a fixed. Colours are now derived at runtime from a 16-colour palette using a deterministic hash of the _type string. This means:
·Any document type — including custom ones — automatically receives a distinct, consistent colour.
·The same type always gets the same colour across renders.
·No configuration is required.
v1.2.0Mar 9, 2026+5~2

+ ✨ Added

+typeLabels — Map raw _type values to human-readable display labels. Applied in both the Type column and the Type filter dropdown; any unmapped type falls back to the raw _type string.
+typeColumnMode — Control how the Type column is rendered.
·'badge' (default) — coloured pill badge, consistent with score badges.
·'text' — plain inline text, useful for dense layouts.
+titleField — Specify which document field to use as the display title in the dashboard. Supports a single field name for all types, or a per-type map. Unmapped types always fall back to title.
+docBadge — Callback that renders a custom badge inline with the document title. Receives the full document and returns { label, bgColor?, textColor?, fontSize? } or undefined to render nothing.
+content.loadingLicense, content.loadingDocuments, content.noDocuments — Custom text for the three loading/empty states of the dashboard. Grouped under the existing content block so all text-content customisation is in one place.

~ 🔄 Changed

~emptyState removed — The emptyState config block has been removed. Its three keys (loadingLicense, loadingDocuments, noDocuments) are now part of the content block (see above). Migrate by moving the keys under content.
~Meta description threshold unified — The valid meta description length range is now consistently 120–160 characters across both the inline field feedback and the Health Dashboard score. Previously the two checks used different lower bounds.
v1.1.1Mar 8, 2026~3

~ 🔄 Changed

~License validation — differentiated invalid-key UI: Users who supply a licenseKey that fails validation now see a distinct "Invalid License Key" screen (❌) with a message explaining the key is invalid or revoked, rather than the generic upgrade prompt shown when no key is provided at all.
~License validation — no-key prompt improved: When licenseKey is omitted entirely, the lock screen now shows a full sanity.config.ts code snippet demonstrating exactly where to add the key, making first-time setup clearer.
~License validation — manual cache bypass: Added a "Click here" button on the invalid-key screen that clears the sessionStorage cache entry for the current project and immediately re-runs the validation request against the server. This avoids the 1-hour cache window when a user has just updated their key without needing a full page reload.
v1.1.0Mar 7, 2026+1

+ ✨ Added

+SEO Health Dashboard — a new built-in Studio tool (📊 SEO Health Dashboard) that gives a bird's-eye view of SEO completeness across all documents that have an seo field.
·Summary stat cards showing total documents, average score, and counts per health tier (Excellent / Good / Fair / Poor / Missing).
·Per-document score (0–95) calculated from meta title, meta description, OG title, OG description, Twitter title, Twitter description, and robots settings.
·Color-coded score badges: green ≥ 80, amber ≥ 60, orange ≥ 40, red < 40.
·Inline issue list — up to 2 top issues surfaced per row with overflow count.
·Clickable document titles that open the document directly in the Sanity desk in a new tab.
·Live search, status filter (All / Excellent / Good / Fair / Poor / Missing), and sort controls (by score or title).
v1.0.10Dec 14, 2025+31

+ ✨ Added

+Documented the full set of fieldOverrides and fieldVisibility keys in README field configuration guidance.
+Expanded Field Specifications in README to cover canonical URLs, meta attributes, default share images, keywords, Open Graph metadata, Twitter branding updates, and robots toggles now available in Studio.
+Rebuilt schema reference docs listing the available schema factories and their usage examples.

❌ Removed

Removed references to the dist/types entry point and legacy TypeScript import snippets from documentation, clarifying that only the plugin bundle is exported.
v1.0.9Dec 6, 2025+1~4×1

+ ✨ Added

+Exported the FeedbackType helpers for use across validation-driven UI components.

~ 🔄 Changed

~Hardened plugin configuration typings, including the new ValidHiddenFieldKeys guard and stricter fieldOverrides typing.
~Returned explicit SchemaTypeDefinition instances from the schema factories, ensuring config is forwarded while keeping Sanity typings intact.
~Refreshed SEO form inputs to rely on shared feedback types, memoised keyword lookups, and clearer schema option typing.
~Reordered public exports so schema factories are grouped consistently with the plugin entry point.

× 🐛 Fixed

×Ensured config-driven hidden logic reaches nested Open Graph and X image selectors. #2
v1.0.8Oct 15, 2025×1

× 🐛 Fixed

×Fixed incorrect initial value assignment in the SEO preview field schema — the value is now correctly set to an empty string.
v1.0.7Oct 12, 2025·9·3

· Plugin Configuration Interface Updates

·twitterImageType - Missing field key for X image type selection
·twitterImageUrl - Missing field key for X external image URL
·SeoFieldsPluginConfig.seoPreview - Now supports both boolean and object with prefix function
·SeoFieldsPluginConfig.baseUrl - New string option for setting preview base URL
·SeoFieldsPluginConfig.fieldVisibility - Per-document-type field visibility control
·SeoFieldsPluginConfig.defaultHiddenFields - Global field hiding capability
·AllFieldKeys - Union type covering all 27+ field keys across SEO, Open Graph, and Twitter
·FieldVisibilityConfig - Interface for field visibility configuration per document type
·SeoFieldConfig - Interface for customizing field titles and descriptions

· Configuration Usage Examples

·https://your-domain.com/{slugified-prefix}/{document_slug}
·For doc: { \_type: 'page', slug: { current: 'about-us' } }
·https://your-domain.com/page/about-us
·For doc: { \_type: 'post', slug: { current: 'hello-world' } }
·https://your-domain.com/article/hello-world
v1.0.6Oct 8, 2025+7~5·3·1

+ ✨ Added

+Added fieldVisibility configuration option for controlling field visibility per document type
+New FieldVisibilityConfig interface for type-safe field visibility control
+defaultHiddenFields configuration option to globally hide specific fields
+New field keys: SitewideFieldKeys including openGraphSiteName and twitterSite
+Updated AllFieldKeys type to include both SEO and sitewide field keys
+Enhanced field utility functions: isFieldHidden and getFieldHiddenFunction
+Dynamic field visibility based on document type through fieldVisibility configuration

~ 🔄 Changed

~Updated Twitter schema title from "Twitter" to "X (Formerly Twitter)"
~Updated Twitter field titles to reference "X" instead of "Twitter":
·"Site Twitter Handle" → "Site X Handle"
·"Twitter Title" → "X Title"
·"Twitter Creator Handle" → "X Creator Handle"
·"Twitter Description" → "X Description"
·"Twitter Image" → "X Image"
~Updated field descriptions to reference "X (formerly Twitter)"
~Enhanced Twitter and OpenGraph schemas to accept configuration parameters
~Added field visibility controls to openGraphSiteName and twitterSite fields using getFieldHiddenFunction

· Technical Improvements

·Refactored schema types to accept and utilize plugin configuration
·Enhanced field utilities with better type safety and configuration support
·Improved plugin architecture with granular field control capabilities

· 🔗 References

·Add field visibility feature and update Twitter branding to X #1 by @crimsonwebteam
v1.0.5Oct 5, 2025+2×1

+ ✨ Added

+Added creator field to Twitter TypeScript type for content creator attribution
+Updated Twitter type to include creator handle field for better Twitter Card support

× 🐛 Fixed

×Fixed missing creator field in Twitter TypeScript type definition
v1.0.4Oct 4, 2025+3

+ ✨ Added

+Added url field to Open Graph schema for canonical URL specification
+Updated OpenGraph TypeScript type to include url field
+Enhanced Open Graph settings with proper URL handling for social media sharing
v1.0.3Sep 24, 2025+5~2

+ ✨ Added

+Enhanced field override examples and documentation
+Support for multiple field override patterns with fieldOverrides configuration
+Improved plugin configuration documentation with real-world usage examples
+Basic configuration example: customize individual field titles and descriptions
+Advanced configuration example: custom meta attributes with field overrides

~ 🔄 Changed

~Refined configuration examples to demonstrate both basic and advanced usage patterns
~Clarified field override behavior for custom meta attributes
v1.0.2Sep 23, 2024+7~2

+ ✨ Added

+Enhanced plugin configuration with customizable field titles and descriptions
+New SeoFieldsPluginConfig interface for type-safe configuration
+fieldOverrides configuration option to customize individual field properties
+Support for customizing all SEO field types: title, description, canonicalUrl, metaImage, keywords
+Full TypeScript support for configuration options
+New SeoField, SeoFieldKeys types exported for advanced usage
+Introduced seoPreview configuration options if wanted to enable/disable the SEO preview feature | default is true.

~ 🔄 Changed

~Plugin now accepts detailed configuration object with field customization
~Enhanced TypeScript type definitions for better developer experience

View raw CHANGELOG.md on GitHub ↗

Was this page helpful?