fix: detect destructive non-SSR mount and wrap it in error boundary#158
Merged
Conversation
With `ssr: false`, the App is mounted into the parent element of
`{children}` in Root, and React clears all other content of that element
on the first render. Content the Root rendered next to `{children}` was
silently wiped in production builds while surviving in dev, where the
full tree is rendered by React (#143).
- Report a console error listing the content that the mount destroys.
The check also runs in dev by inspecting the served shell HTML (which
has the same shape as the production build) before React replaces the
document, so the problem is surfaced on every dev page load too.
Whitespace, comments, and framework-emitted script elements are
exempt.
- Wrap the prod non-SSR mount in StrictMode and GlobalErrorBoundary
like the other three mount paths, using a new embedded fallback since
the mount point is an inner element where the document-level
fallback's <html> would be invalid.
- Document the "keep {children} alone in its parent element" constraint
in How It Works, Getting Started, SSR docs, FAQ, and the bundled
knowledge skill.
Closes #143
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HADYAC46o2XCQPkCpavDyt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #143
Problem
With
ssr: false, the App is mounted into the parent element of{children}in Root, and React clears all other content of that element on the first render. Content the Root rendered next to{children}was silently wiped in production builds while surviving in dev (where the full tree is rendered by React). Additionally, the prod non-SSR mount path was the only one of the four mount paths withoutGlobalErrorBoundaryandStrictMode.Per discussion, this PR takes the documentation + runtime validation approach rather than introducing a framework-owned wrapper element.
Changes
Runtime validation (
src/client/mount-validation.ts)console.erroris reported listing the content that the mount destroys, with a link to the docs. The mount still proceeds (warning, not a hard failure, since the check runs in end users' browsers).createRoot(document)replaces the document. The marker is located by its id prefix, so no manifest plumbing is needed. This surfaces the problem on every dev page load, closing the dev/prod divergence.<script>elements are exempt — the framework itself emits bootstrap/RSC-payload scripts next to the marker when Root uses<body>{children}</body>.Error boundary / StrictMode
<StrictMode><GlobalErrorBoundary>like the other three paths.GlobalErrorBoundarygained anembeddedprop: the existing fallback renders a full<html><body>document, which would be invalid when mounted into an inner container element, so the embedded variant renders the same content in a<div>instead.Docs
{children}Alone in Its Parent Element" section in How It Works (linked from the console error), a Getting Started bullet, a "No Root Layout Constraint" pro on the SSR page, a FAQ entry keyed to the error message, and a note in the bundled knowledge skill.Tests
destructive.htmlentry in the multi-entry e2e fixture with a violating Root:Verification
pnpm typecheck,pnpm lint,pnpm format:checkclean🤖 Generated with Claude Code
https://claude.ai/code/session_01HADYAC46o2XCQPkCpavDyt
Generated by Claude Code