Developers

SEO marking

Mark page SEO fields with data-macro-seo, identify the page with data-macro-page, and let editors manage title, OG, canonical, and robots in the overlay.

Page-scoped SEO
SEO values are stored as content nodes such as page.contact.seo.title. Locale lives in Macro’s i18n layer — not inside the key — same as body content.

Page identity

Macro needs a stable page slug to scope SEO keys. Resolution order:

  1. data-macro-page on <html>

    Preferred. Use a decoded slug; nested paths are fine (blog/2026/post).

    html
    <html lang="en" data-macro-page="contact">
  2. resolvePageSlug in config

    Optional function when the URL shape does not match your content model.

    macro.config.js
    export const macroConfig = {
      // ...
      resolvePageSlug: (pathname) => {
        if (pathname.startsWith('/blog/')) return pathname.slice(1).replace(/\.html$/, '')
        return pathname.replace(/^\/|\.html$/g, '') || 'home'
      },
    }
  3. URL heuristic

    Fallback: /contact.htmlcontact, /home.

Slugs that contain / are encoded in keys: blog/2026/macro-seopage.blog__2026__macro-seo.seo.title.

Head markup

Mark each SEO field with data-macro-seo. Fallback text/attributes ship in HTML and are overwritten when Macro has a value.

contact.html
<html lang="en" data-macro-page="contact">
  <head>
    <title data-macro-seo="title">Contact — Example</title>
    <meta
      name="description"
      data-macro-seo="description"
      content="Default description"
    />
    <meta property="og:title" data-macro-seo="ogTitle" content="Contact — Example" />
    <meta
      property="og:description"
      data-macro-seo="ogDescription"
      content="Default OG description"
    />
    <meta
      property="og:image"
      data-macro-seo="ogImage"
      content="https://example.com/og.jpg"
    />
    <link
      rel="canonical"
      data-macro-seo="canonical"
      href="https://example.com/contact.html"
    />
    <meta name="robots" data-macro-seo="robots" content="index, follow" />
  </head>
  <body>
    <!-- page body -->
  </body>
</html>

Injection contract

NameTypeDescription
title<title>Written to textContent
description<meta>Written to content
ogTitle<meta>property="og:title" → content
ogDescription<meta>property="og:description" → content
ogImage<meta>property="og:image" → content (absolute https URL)
canonical<link>rel="canonical" → href
robots<meta>name="robots" → content
  • ogImage must resolve to an absolute https:// URL in production HTML.
  • Empty canonical in Macro → the HTML fallback is left alone.
  • robots values: index,follow · noindex,follow · index,nofollow · noindex,nofollow.

Runtime vs build

At runtime the SDK hydrates [data-macro-seo]:not([data-macro-ssr]) from GET /content/seo with the public key. For crawler-ready HTML at first byte, use Prerender (or Bake for offline exit).

Related