fix(OUT-3643): avoid editor hydration error - #202
Conversation
…ation error Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR fixes hydration mismatches in
Confidence Score: 5/5Safe to merge — the change correctly defers editor rendering until after client hydration, and all hook calls remain unconditional before the early return. The mounted guard is a well-understood pattern for suppressing hydration mismatches in Next.js client components. All hooks are called in the same order on every render before the conditional return. The blank-until-mounted behaviour was acknowledged as intentional by the author. No logic errors, missing guards, or data correctness issues were found. No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "Update src/features/editor/components/Cl..." | Re-trigger Greptile |
| if (!mounted) { | ||
| return null | ||
| } |
There was a problem hiding this comment.
Guaranteed blank flash on every page load
Because this is a 'use client' component that Next.js still SSR-renders, the server also runs the component with mounted = false and emits null into the initial HTML. On the client, the first hydration pass also returns null (matching the server), so there is no mismatch — but that means the entire editor area is absent from the page until the useEffect fires post-hydration. Every visitor on every load will see a blank space where the editor should be, which is a layout shift regardless of connection speed.
A narrower fix is to add suppressHydrationWarning directly on the <div> that carries the dynamic backgroundColor, which silences the mismatch only for that attribute while keeping the SSR-rendered markup visible. Alternatively, wrapping the import in dynamic(() => import('./ClientEditorWrapper'), { ssr: false }) at the call site explicitly opts out of SSR and makes the blank-until-mounted behaviour intentional and contained.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
@greptileai the issue is there are lots of places where hydration mismatch. So it is not a good idea to patch all these doms and also ensure future changes do not bring the issue again.
There was a problem hiding this comment.
That's a fair point — if the editor has many sources of hydration mismatch (dynamic colors, user preferences, theme state, etc.), patching each DOM node is not scalable and won't guard against future regressions.
The concern with the current approach isn't the strategy, it's that SSR still emits null into the HTML (since the server runs this component too and hits mounted = false). The blank flash is unavoidable with this pattern.
The fix that achieves your goal — skip SSR for the whole editor, centralized in one place — is dynamic with ssr: false at the call site:
// wherever ClientEditorWrapper is imported
const ClientEditorWrapper = dynamic(
() => import('./ClientEditorWrapper').then(m => m.ClientEditorWrapper),
{ ssr: false }
)Then you can remove the mounted gate entirely. The result is identical behavior (blank until JS hydrates), but:
- The intent is explicit and colocated with the import
- No SSR output means no hydration reconciliation attempt at all — all future mismatches inside the component are also ignored
- The component itself stays clean
The blank flash exists in both approaches, but ssr: false makes it deliberate and contained rather than an emergent side effect of the mounted check.
Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
|
@greptileai re review the pr again. |
What changed
ClientEditorWrappernow gates its render on a clientmountedflag, so the editor only renders after hydration — fixes the hydration mismatch from server/clientbackgroundColordifferences.mise.toml: added adevtask that runs the dev server via portless.What to review
mountedgate doesn't introduce a visible flash/layout shift on first paint (returnsnulluntil mounted).🤖 Generated with Claude Code