Fields

SEO Field

The SEO field renders a structured metadata editor in the admin UI and stores its value as a JSON object in Convex. It provides live SERP and social card previews so editors can see how their content will appear in search results and social media shares. The underlying Convex validator is seoValidator — a v.object() with six optional string fields.

Stored shape

{
  metaTitle?: string;
  metaDescription?: string;
  canonicalUrl?: string;
  ogTitle?: string;
  ogDescription?: string;
  ogImage?: string;
}

The exported seoValidator and SeoValue type can be imported from "vextro" for use in frontend code.

Config options

OptionTypeDefaultDescription
requiredbooleanfalseMakes the field required in both the schema and the admin UI.
readOnlybooleanfalseRenders the editor as disabled.
siteUrlstring"https://example.com"Base URL shown in the SERP preview snippet.
labelstring | (data) => stringAuto-generatedCustom label for the admin UI.
descriptionstring—Help text displayed below the label.
conditionFieldCondition—Conditionally show or hide this field.
sidebarbooleanfalsePlace this field in the document sidebar.
copyablebooleanfalseShow a copy-to-clipboard button.

Example usage

import { f, defineVextroCollection } from "vextro";

export const pages = defineVextroCollection({
  slug: "pages",
  label: "Pages",
  collectionType: "content",
  tableName: "pages",
  fields: {
    title: f.text({ required: true }),
    slug: f.slug({ sourceField: "title", required: true }),
    body: f.richText(),
    seo: f.seo({
      siteUrl: "https://example.com",
      description: "Search engine and social media metadata.",
    }),
  },
});

Admin behavior

The SEO field renders as a collapsible panel with six sub-fields. Below the inputs, two live preview cards are shown:

  • SERP preview — simulates a Google search result using metaTitle, metaDescription, and the siteUrl + page slug.
  • Social card preview — simulates an Open Graph card using ogTitle, ogDescription, and ogImage.

When ogTitle or ogDescription are left empty, the previews fall back to metaTitle and metaDescription.

Frontend usage

Use the stored SEO object to populate <meta> tags in your Astro layout:

---
const { seo } = Astro.props;
---
<head>
  {seo?.metaTitle && <title>{seo.metaTitle}</title>}
  {seo?.metaDescription && <meta name="description" content={seo.metaDescription} />}
  {seo?.canonicalUrl && <link rel="canonical" href={seo.canonicalUrl} />}
  {seo?.ogTitle && <meta property="og:title" content={seo.ogTitle} />}
  {seo?.ogDescription && <meta property="og:description" content={seo.ogDescription} />}
  {seo?.ogImage && <meta property="og:image" content={seo.ogImage} />}
</head>

Validator import

import { seoValidator } from "vextro";
import type { SeoValue } from "vextro";
Previous
Custom
Next
File