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
| consumer | reads | source |
|---|
SERP <title> | page.data.title + %s | ObjectStack | apps/docs/app/[lang]/docs/[[...slug]]/page.tsxgenerateMetadata; apps/docs/app/layout.tsxmetadata.title.template |
on-page <h1> | page.data.title | same page: <DocsTitle>{page.data.title}</DocsTitle> |
| sidebar label | page.data.title | fumadocs-core page-tree builder, buildFile(): name: title ?? pathToName(basename(path, extname(path))) |
llms.txt heading | page.data.title | apps/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
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.
One-liner
A doc page's frontmatter
titleis simultaneously the SERP<title>, the on-page<h1>, the sidebar nav label and thellms.txtheading. 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<title>page.data.title+%s | ObjectStackapps/docs/app/[lang]/docs/[[...slug]]/page.tsxgenerateMetadata;apps/docs/app/layout.tsxmetadata.title.template<h1>page.data.title<DocsTitle>{page.data.title}</DocsTitle>page.data.titlefumadocs-corepage-tree builder,buildFile():name: title ?? pathToName(basename(path, extname(path)))llms.txtheadingpage.data.titleapps/docs/lib/source.tsgetLLMText()Why no zero-code workaround exists:
pageSchema(fumadocs-core/dist/source/schema.js) declares exactlytitle,description,icon,full,_openapi, and compiles toz.core.$strip— an inventedsidebarTitle:in frontmatter is silently dropped, not rejected.apps/docs/source.config.tsusespageSchemaunextended.meta.json(metaSchema) has atitle, butbuildFolder()uses it for the folder's own label (node.name = metadata.title ?? node.index?.name). It cannot name a child page.meta.jsonpagesaccepts a[Label](url)form, butresolveLink()emits a bare link node with no$ref— losingdescription,iconand 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'sindex.mdxfromchildrenunlessmeta.jsonpagesnames"index", and the folder's own label comes frommeta.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") andprotocol/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:
Then a page can carry
title: Object metadata — define your data schemafor search andsidebarTitle: Objectsfor navigation.Acceptance
title, and the sidebar shows the short one while<title>shows the long onetitle)apps/docstypecheck stays greenScope note
apps/docs/**is inside epic #12243's declared territory but outside #12237's file surface (content/docs/**frontmattertitle+meta.json), andapps/docs/lib/source.tsis 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:
It cannot. Unassigned — recording, not claiming.