Skip to content

fix(publish): wire entry SEO title and description into the published <head> - #419

Merged
DavidBabinec merged 1 commit into
mainfrom
fix/entry-seo-meta-tags
Aug 21, 2026
Merged

fix(publish): wire entry SEO title and description into the published <head>#419
DavidBabinec merged 1 commit into
mainfrom
fix/entry-seo-meta-tags

Conversation

@DavidBabinec

Copy link
Copy Markdown
Contributor

The bug

Creating a post while the entries list was still loading left a nameless row in the Content sidebar. The entry saved correctly and the editor showed its "Untitled" placeholder, but the sidebar row rendered with only an author and a status. The same click worked most of the time, because which request finished first decided the outcome.

The fix

createUntitledEntry stores "Untitled" on the server but hands the editor a copy whose title is blank, so the title field shows its placeholder. loadEntries merged that editor copy back into the list whenever an in-flight load resolved after the create. The workspace now keeps the stored row alongside the editor view and rebuilds the list from the stored one. An MCP save, where the selection genuinely is the newer row, still merges as before.

Verification

bun test                                    # 6618 pass, 0 fail (3 runs)
bun run build                               # clean
bun run lint                                # clean
bunx playwright test content-create-race    # 6 passed

New coverage at unit and browser level, registered as CONTENT-009. Both hold the list response until the create lands, and both fail without the fix.

… <head>

`seoTitle` and `seoDescription` are fully-built post-type fields, declared
in the default field set, seeded by both migration paths, and editable in
the Content settings panel. Neither reached the published page. An entry's
`<title>` always showed its plain title, and no `<meta name="description">`
was emitted at all.

Give `publishPage` a `documentMeta` override consumed by
`buildDocumentMetaTags`. Both entry render paths read the row's authored
values with `readEntrySeoOverride` and pass them there, so publish and the
Content editor's Live mode stay in parity. The override outranks the
site-level metaTitle / metaDescription, because a per-entry value is the
more specific choice. A blank field is not an override and still falls
through.

The override deliberately never touches `page.title`. `publishPage` feeds
that to `buildPageFrame`, so it is also the `{page.title}` binding, and an
SEO value assigned there renders inside the page body. `page.title` keeps
the entry's own title for both title bindings.

Co-authored-by: Aaron Sachs <898627+asachs01@users.noreply.github.com>
@DavidBabinec
DavidBabinec merged commit a0b1e4e into main Aug 21, 2026
9 checks passed
mostafasadeghidev added a commit to mostafasadeghidev/Instatic that referenced this pull request Aug 22, 2026
…ompose

CoreBunch#419 landed while this sat open and rewrote the same function. It gives
`buildDocumentMetaTags` a `documentMeta` override so a post-type entry's
authored `seoTitle` / `seoDescription` reach the published `<head>`.

That is a different feature from this branch's, not a replacement. CoreBunch#419
answers "this row has its own title"; this answers "every row of this
template shares one pattern". An author wants whichever suits the site,
and on most sites both: a handful of rows worth naming individually,
the rest covered by `{currentEntry.name} | Acme`.

So the two compose rather than pick a winner. The fallback chain is
upstream's — override → metaTitle → page.title → site.name — and whatever
wins is then interpolated against the render context. A row's authored SEO
title may itself carry tokens, which neither branch offered alone.

418 publisher tests green, covering both sides.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mostafasadeghidev added a commit to mostafasadeghidev/Instatic that referenced this pull request Aug 22, 2026
Seven files collided, and six of them for a happy reason: CoreBunch#348 merged, so
upstream now carries this stack's own loop-filter code. Those took
upstream's copy — which is ours plus what review added on top of it:
`isCellComparableField` and `withoutCellFilter`, field pickers that hide
fields a cell condition cannot address, clearer operator labels, and a
dual-dialect test. `cellFilterMatches` went with it; nothing imported it.

The seventh is `render.ts`, where CoreBunch#419 gave `buildDocumentMetaTags` a
`documentMeta` override so an entry's authored SEO title reaches the
`<head>`. That answers "this row has its own title"; the stack's CoreBunch#337
answers "every row of this template shares one pattern". Both are wanted,
so the fallback chain is upstream's and whatever wins is then interpolated
against the render context — a row's authored SEO title may itself carry
tokens, which neither side offered alone.

The README's pending table drops CoreBunch#348 into the landed section.

Verified: tsc, build and lint clean; publisher and templates 505/505;
fork-stack-capabilities 12/12. The remaining noise is Windows temp-file
locking in test teardown, which reproduces on an untouched upstream
checkout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant