Skip to content

fix(ssg-md): assign useEffect and useLayoutEffect noop value when renderToMarkdownString to match real SSR behavior (renderToString)#3121

Merged
SoonIter merged 1 commit intomainfrom
syt/fix-ssg-md-render-cycle
Feb 10, 2026
Merged

fix(ssg-md): assign useEffect and useLayoutEffect noop value when renderToMarkdownString to match real SSR behavior (renderToString)#3121
SoonIter merged 1 commit intomainfrom
syt/fix-ssg-md-render-cycle

Conversation

@SoonIter
Copy link
Copy Markdown
Member

@SoonIter SoonIter commented Feb 10, 2026

Summary

assign useEffect and useLayoutEffect noop value when renderToMarkdownString

Related Issue

Sometimes SSG-MD will throw a 'window is not defined' error, but SSG won't.

image

Root Cause

React's renderToString uses Fizz, which has its own HooksDispatcher where useEffect/useLayoutEffect are noop. Our renderToMarkdownString uses react-reconciler which goes through the full fiber render→commit cycle and executes effects.

Copilot AI review requested due to automatic review settings February 10, 2026 07:05
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Feb 10, 2026

Deploying rspress-v2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 11825ed
Status: ✅  Deploy successful!
Preview URL: https://c972eb90.rspress-v2.pages.dev
Branch Preview URL: https://syt-fix-ssg-md-render-cycle.rspress-v2.pages.dev

View logs

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 10, 2026

Rsdoctor Bundle Diff Analysis

Found 3 projects in monorepo, 0 projects with changes.

📊 Quick Summary
Project Total Size Change
node 11.9 MB 0
web 15.8 MB 0
node_md 1.5 MB 0

Generated by Rsdoctor GitHub Action

@SoonIter SoonIter changed the title fix(core): suppress effects in renderToMarkdownString to match SSR behavior fix(ssg-md): suppress effects in renderToMarkdownString to match real SSR behavior (renderToString) Feb 10, 2026
@SoonIter SoonIter force-pushed the syt/fix-ssg-md-render-cycle branch from f595f54 to 5fcac8f Compare February 10, 2026 07:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to make renderToMarkdownString behave like React SSR (renderToString / Fizz) by ensuring React effects never execute during markdown rendering, preventing Node-side crashes such as document is not defined.

Changes:

  • Intercepts React’s hooks dispatcher (ReactSharedInternals.H) during renderToMarkdownString to no-op useEffect, useLayoutEffect, and useInsertionEffect.
  • Wraps the render+commit path in a try/finally to restore the dispatcher after rendering.
  • Adds a comprehensive test suite asserting that effects (and cleanups) never run while other hooks still work.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/core/src/node/ssg-md/react/render.ts Adds a hooks-dispatcher interceptor to suppress effects during reconciler rendering and restores it afterward.
packages/core/src/node/ssg-md/react/render.test.tsx Adds tests verifying SSR-like effect suppression (including insertion effects and cleanup).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/core/src/node/ssg-md/react/render.ts
Comment thread packages/core/src/node/ssg-md/react/render.ts
@SoonIter SoonIter changed the title fix(ssg-md): suppress effects in renderToMarkdownString to match real SSR behavior (renderToString) fix(ssg-md): assign useEffect and useLayoutEffect noop value when renderToMarkdownString to match real SSR behavior (renderToString) Feb 10, 2026
…ng to match SSR behavior

React's renderToString (Fizz) uses a HooksDispatcher where useEffect and
useLayoutEffect are noop. Our renderToMarkdownString uses react-reconciler
which registers and executes effects, causing "document is not defined" in
Node.js. Fix by intercepting ReactSharedInternals.H during the render pass
to make useEffect/useLayoutEffect/useInsertionEffect no-ops.
@SoonIter SoonIter force-pushed the syt/fix-ssg-md-render-cycle branch from 5fcac8f to 11825ed Compare February 10, 2026 07:17
@SoonIter
Copy link
Copy Markdown
Member Author

it seems no change to existing projects

image

@SoonIter SoonIter enabled auto-merge (squash) February 10, 2026 07:45
@SoonIter SoonIter merged commit 54db221 into main Feb 10, 2026
6 checks passed
@SoonIter SoonIter deleted the syt/fix-ssg-md-render-cycle branch February 10, 2026 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants