Skip to content

fix(publish): wire the existing seoTitle field into the document <title> - #370

Closed
asachs01 wants to merge 1 commit into
CoreBunch:mainfrom
wyre-technology:fix/entry-seo-title-meta-tag
Closed

fix(publish): wire the existing seoTitle field into the document <title>#370
asachs01 wants to merge 1 commit into
CoreBunch:mainfrom
wyre-technology:fix/entry-seo-title-meta-tag

Conversation

@asachs01

Copy link
Copy Markdown
Contributor

Summary

seoTitle is already a fully-built post-type field — declared in buildPostTypeDefaultFields(), seeded by both DB migration paths, editable via the Content Settings panel's SEO field, and even exposed to the token/binding picker ({currentEntry.seoTitle}) and the AI content tool — but the publish and preview render paths never actually consume it.

renderPublishedDataRowTemplate() (server/publish/publicRenderer.ts) and its preview twin handleRowPreview() (server/handlers/cms/data/preview.ts) both set merged.title unconditionally from row.cells.title / draftCells.title. That merged.title is what buildDocumentMetaTags() uses for the <title> tag (src/core/publisher/render.ts), so an author who fills in a distinct SEO title today sees zero effect on the actual <title> output — the field silently does nothing at publish time.

Change

  • New resolveEntryDocumentTitle(cells) helper in src/core/data/cells.ts: prefers seoTitle when set (non-empty string), falls back to title, returns null when neither is present (preserving today's exact fallback-to-template behavior for rows with no title at all).
  • Both call sites (publicRenderer.ts:182, preview.ts:99) now use this helper instead of the inline title-only check, so publish and preview stay in parity.
  • The on-page H1 / {currentEntry.title} binding is completely untouched — traced the actual data flow and confirmed it resolves from publishedDataRowToLoopItem(row)'s spread of row.cells directly into the template's entry-stack frame, never through merged.title. So this change only ever affects the <title> meta tag, never the visible headline.

Test plan

  • New test in src/__tests__/server/publicRendering.test.ts: a real postTypes-targeted entry template (not the existing null-early-return fixture) proving seoTitle set → <title> uses it while the H1 binding still renders plain title; seoTitle unset → byte-identical fallback.
  • 4 new unit tests for resolveEntryDocumentTitle in src/core/data/__tests__/cells.test.ts.
  • Full suite green: 6616 passed / 1 skipped / 0 failed (bun test).
  • tsc -b clean, eslint . clean.

Noticed while working on the trusted-host iframe-embed fix (#369) — same investigation trail, unrelated bug though, so opened as a separate PR for independent review.

seoTitle is already a fully-built post-type field — declared in the
default field set, seeded by the DB migrations, and editable in the
Content Settings panel's SEO field — but the publish and preview render
paths never consumed it: `renderPublishedDataRowTemplate` and
`handleRowPreview` both set `merged.title` unconditionally from
`row.cells.title`, so the meta-tag `<title>` always fell back to the
plain entry title even when an author filled in a distinct SEO title.

Add `resolveEntryDocumentTitle()` (prefers seoTitle, falls back to
title) and use it at both call sites. The on-page H1 binding
(`{currentEntry.title}`) is unaffected — it resolves from the loop
item's cells directly, never from `merged.title` — so this only
changes what feeds the `<title>` tag / meta description pipeline.
@DavidBabinec

Copy link
Copy Markdown
Contributor

Thanks for this, @asachs01. You found a real bug that had been sitting in plain sight: seoTitle was fully built out and completely unread at publish time.

Landing it took a different layer, so this went in as #419 (merged in a0b1e4e) with you as co-author, and I am closing this one.

Two things surfaced while verifying:

  1. merged.title is not a meta-only channel. publishPage hands the page to buildPageFrame, so page.title is also the {page.title} binding. With the SEO title assigned there, an entry template containing {page.title} rendered Breadcrumb: SEO Override Title in the page body. The PR description's claim that only the <title> tag changes holds for {currentEntry.title}, which you did check, but not for {page.title}.
  2. buildDocumentMetaTags reads settings.metaTitle ?? page.title, so on any site with a Meta Title set in Settings, the per-entry SEO title still had no effect. On those sites the change produced the body leak without the meta-tag fix.

The merged version gives publishPage a documentMeta override consumed by buildDocumentMetaTags. page.title keeps the entry's real title, the authored SEO values reach <head> only, and they outrank the site-level settings because a per-entry value is more specific. The same channel carries seoDescription, which was equally inert: the second field in the same panel emitted no <meta name="description"> at all.

Also added CONTENT-010, a Playwright spec that publishes a post and reads the served <head>. Nothing checked that before, which is how both fields shipped inert.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants