Skip to content

fix(publisher): render current entry body in any content outlet#62

Merged
DavidBabinec merged 1 commit into
mainfrom
fix/outlet-entry-body-binding
Jun 13, 2026
Merged

fix(publisher): render current entry body in any content outlet#62
DavidBabinec merged 1 commit into
mainfrom
fix/outlet-entry-body-binding

Conversation

@DavidBabinec

Copy link
Copy Markdown
Contributor

What changed

A base.outlet only rendered the current entry's body when the node carried a persisted dynamicBindings.html overlay — which was set in exactly one place: the auto-seeded default entry template (buildDefaultTemplateCells).

When a site owner builds their own post template and drags a Content Outlet in by hand, that outlet has props.html: '' and no binding, so the publisher filled it with nothing and it rendered as an empty <main data-instatic-content-region></main>.

The body resolution (resolveDynamicProps) is shared by all three render surfaces, so the gap broke the published page, the content preview API, and the Live Canvas editor alike.

Why

An outlet is, by definition, the hole the current entry's body flows into — there is no UI to bind it and the binding is always the same (currentEntry.body, rendered markdown → HTML). Relying on a persisted overlay that only the seeder set was fragile: any hand-authored outlet was silently broken.

The fix

  • dynamicBindings.ts — new effectiveNodeBindings(node) helper applies the currentEntry.body → html binding implicitly to every base.outlet, merged on top of any persisted overlay.
  • renderNode.ts (publisher) and NodeRenderer.tsx (editor canvas) now resolve through effectiveNodeBindings(node) so all surfaces behave identically.
  • templateSeeding.ts — dropped the now-redundant persisted binding from the seed; a seeded outlet is now identical to a hand-dropped one (single source of truth).

Outside an entry route the entry stack is empty, so the binding resolves to nothing and the outlet stays empty — an everywhere layout's outlet still hosts a whole page.

User / developer impact

Dragging a Content Outlet into a custom post template now renders each post's body in it — on the canvas, in the live editor, and on the published page. No migration needed; existing seeded templates keep working (the implicit binding resolves to the same value).

Verification

  • New src/__tests__/publisher/outletEntryBody.test.ts reproduces the bug (outlet with no persisted binding) and confirms the body renders, plus the inert-outside-entry-routes case.
  • bun test src/__tests__/publisher/ src/core/templates/ server/publish/ → 405 pass.
  • bun run build and bun run lint → clean.
  • react-doctor --scope changed --base main → no new issues.

🤖 Generated with Claude Code

A base.outlet only rendered the post body when the node carried a
persisted dynamicBindings.html overlay, which was set in exactly one
place: the auto-seeded default template. A hand-dropped outlet (custom
template) had no binding, so the publisher filled it with nothing and it
rendered as an empty <main data-instatic-content-region></main> — on the
published page, the content preview, and the Live Canvas alike.

An outlet is, by definition, the hole the current entry's body flows
into; there is no UI to bind it and the binding is always the same. Apply
it implicitly via effectiveNodeBindings() at the shared resolution layer
so every outlet renders the body, and drop the now-redundant persisted
binding from the seed (single source of truth). Outside an entry route
the entry stack is empty, so the binding stays inert and an everywhere
layout's outlet still hosts a whole page.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@DavidBabinec DavidBabinec marked this pull request as ready for review June 13, 2026 10:27
@DavidBabinec DavidBabinec merged commit b9ee769 into main Jun 13, 2026
6 checks passed
@DavidBabinec DavidBabinec deleted the fix/outlet-entry-body-binding branch June 16, 2026 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant