feat: support static client component rendering#1227
Conversation
|
@launchdarkly/js-sdk-common size report |
|
@launchdarkly/js-client-sdk size report |
|
@launchdarkly/js-client-sdk-common size report |
|
@launchdarkly/browser size report |
|
@cursor review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| addHook: noop, | ||
| waitForInitialization: noopPromise, | ||
| setStreaming: noop, | ||
| start: noopPromise, |
There was a problem hiding this comment.
Noop client methods resolve to undefined breaking API contract
Medium Severity
The noopPromise (() => Promise.resolve()) is used for start, waitForInitialization, and flush, which all have non-void return types in the LDClient interface. start() and waitForInitialization() are typed to return Promise<LDWaitForInitializationResult>, and flush() returns Promise<{ error?: Error; result: boolean }>. Resolving to undefined means any code that accesses .status or .result on the resolved value will throw a TypeError at runtime. The old noop client returned proper result objects (e.g., { status: 'failed', error } for start/waitForInitialization). The as unknown as LDReactClient cast hides this mismatch from the compiler.
There was a problem hiding this comment.
per previous comments - this should not be an issue... we can fix this if static rendering breaks.
2998425 to
3d2d627
Compare
3d2d627 to
82d02de
Compare
| // static rendering which will attempt to render client code during build time. In these cases, | ||
| // we will need to use the noop client to avoid errors. | ||
| if (typeof window === 'undefined') { | ||
| return createNoopClient(); |
There was a problem hiding this comment.
🔴 Bootstrap data is never passed to createNoopClient during SSR
When running server-side (e.g., Next.js SSR/static rendering), createClient at LDReactClient.tsx:52 calls createNoopClient() with no arguments, discarding any bootstrap data the user provided. The bootstrap data flows through createLDReactProvider (LDReactProvider.tsx:123-129) into client.start({ bootstrap }), but the noop client's start is a no-op (() => Promise.resolve()) that ignores its arguments.
As a result, during SSR:
isReady()returnsfalseinstead oftruegetInitializationState()returns'initializing'instead of'complete'- All variation hooks (via
useVariationCore.ts:14,27-30) seeready === falseand return default values instead of bootstrap flag values
createNoopClient was clearly designed to support bootstrap (it accepts a bootstrap parameter, extracts flags, sets hasBootstrap, and conditions isReady/getInitializationState on it), but no code path ever supplies it.
SSR flow showing bootstrap data loss
In createLDReactProvider (LDReactProvider.tsx:122-132):
createClient(id, ctx, ldOptions)→ detects SSR → returnscreateNoopClient()(no bootstrap)client.start({ bootstrap: data })→ noopstartignores the argument- Bootstrap data is lost; noop client behaves as if no bootstrap was provided
Prompt for agents
In packages/sdk/react/src/client/LDReactClient.tsx, the SSR path at line 52 calls createNoopClient() without any bootstrap data. The fix needs to propagate bootstrap data from createLDReactProvider to createNoopClient during SSR. There are several approaches:
1. Add a bootstrap parameter to createClient's options (e.g., extend LDReactClientOptions or add a separate parameter), and pass it through to createNoopClient when SSR is detected. Then in createLDReactProvider (LDReactProvider.tsx:125), pass the bootstrap data via createClient.
2. Alternatively, detect SSR in createLDReactProvider itself (before calling createClient) and create the noop client directly with the bootstrap data, bypassing createClient entirely.
The key requirement is that when bootstrap data is available during SSR, createNoopClient(bootstrap) must be called instead of createNoopClient().
Was this helpful? React with 👍 or 👎 to provide feedback.


To support static client rendering, we will need to introduce a proper noop client for the client sdk since that will be ran on server side during build time.
Note
Medium Risk
Changes
createClientbehavior whenwindowis undefined and alters server-side initialization/error semantics, which could affect SSR/static rendering and early flag reads. Risk is moderate due to potential subtle differences in hydration-time behavior and expected initialization state.Overview
Enables static/SSR rendering of React client components by having
createClientreturn a dedicatedcreateNoopClientstub whenwindowis undefined, avoiding runtime errors during build-time rendering.Adds
createNoopClient(bootstrap?), which can ad-hoc return flag values from provided bootstrap data (excluding$metadata), reports initialization ascompleteonly when bootstrap is provided, and otherwise staysinitializingwith no initialization error. Updates and expands tests to cover the new noop client behavior and bootstrap-based variation results.Written by Cursor Bugbot for commit 2998425. This will update automatically on new commits. Configure here.