Skip to content

docs site: frontmatter carries no short sidebar label — one title serves SERP, h1, nav and llms.txt #12311

Description

@os-zhuang

One-liner

A doc page's frontmatter title is simultaneously the SERP <title>, the on-page <h1>, the sidebar nav label and the llms.txt heading. There is no field that separates them, so any SEO-driven lengthening of a title lengthens the navigation entry by exactly as much. #12237 is blocked on this for 176 of its 180 editable pages.

Measured — fumadocs-core@16.14.4, this repo's actual configuration

consumerreadssource
SERP <title>page.data.title + %s | ObjectStackapps/docs/app/[lang]/docs/[[...slug]]/page.tsxgenerateMetadata; apps/docs/app/layout.tsxmetadata.title.template
on-page <h1>page.data.titlesame page: <DocsTitle>{page.data.title}</DocsTitle>
sidebar labelpage.data.titlefumadocs-core page-tree builder, buildFile(): name: title ?? pathToName(basename(path, extname(path)))
llms.txt headingpage.data.titleapps/docs/lib/source.tsgetLLMText()

Why no zero-code workaround exists:

  • pageSchema (fumadocs-core/dist/source/schema.js) declares exactly title, description, icon, full, _openapi, and compiles to z.core.$strip — an invented sidebarTitle: in frontmatter is silently dropped, not rejected. apps/docs/source.config.ts uses pageSchema unextended.
  • meta.json (metaSchema) has a title, but buildFolder() uses it for the folder's own label (node.name = metadata.title ?? node.index?.name). It cannot name a child page.
  • meta.jsonpages accepts a [Label](url) form, but resolveLink() emits a bare link node with no $ref — losing description, icon and the page↔tree binding, and the real page would need !-excluding to avoid appearing twice. A link mechanism, not a label mechanism.

The only exemption available today: buildFolder() omits a folder's index.mdx from children unless meta.jsonpages names "index", and the folder's own label comes from meta.jsontitle. Enumerated across all 19 editable folders, exactly 3 pages qualify — content/docs/index.mdx, protocol/objectql/index.mdx, protocol/objectui/index.mdx. The other 16 folders list "index" explicitly.

Note that protocol/objectql/meta.json ("title": "Data Protocol") and protocol/objectui/meta.json ("title": "UI Protocol") already demonstrate the short-label/long-title split this issue asks to generalise.

Expected

Extend the docs collection's frontmatter with an optional short nav label and teach the page tree to prefer it — roughly ten lines, and the blog collection in the same file already shows the extension idiom:

// apps/docs/source.config.ts
docs: {schema: pageSchema.extend({sidebarTitle: z.string().optional()}), ... }
// apps/docs/lib/source.tsexportconstsource=loader({
...,pageTree: {transformers: [{file(node,filePath){constshort=filePath&&this.storage.read(filePath)?.data?.sidebarTitle;returnshort ? { ...node,name: short} : node;},}],},});

Then a page can carry title: Object metadata — define your data schema for search and sidebarTitle: Objects for navigation.

Acceptance

  • a page may declare a nav label distinct from its title, and the sidebar shows the short one while <title> shows the long one
  • a page that declares no short label is unchanged (falls back to title)
  • the field is a real declaration, not silently stripped — adding it to a page and reading the rendered nav proves it
  • apps/docs typecheck stays green

Scope note

apps/docs/** is inside epic #12243's declared territory but outside #12237's file surface (content/docs/** frontmatter title + meta.json), and apps/docs/lib/source.ts is shared with cards in flight — so this is filed rather than taken there.

Source

Found while implementing #12237 (page titles), which the card itself gated on this question:

check whether content/docs/**/meta.json or the frontmatter can carry a separate short label for navigation before lengthening every sidebar entry

It cannot. Unassigned — recording, not claiming.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions