Skip to content

Commit 7c2cb07

Browse files
[FSSDK-10777] ssr support update
1 parent 6a836f5 commit 7c2cb07

3 files changed

Lines changed: 310 additions & 73 deletions

File tree

README.md

Lines changed: 47 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ Refer to the [React SDK's developer documentation](https://docs.developers.optim
1212

1313
For React Native, review the [React Native developer documentation](https://docs.developers.optimizely.com/feature-experimentation/docs/javascript-react-native-sdk).
1414

15-
1615
### Features
1716

1817
- Automatic datafile downloading
@@ -28,11 +27,7 @@ The React SDK is compatible with `React 16.8.0 +`
2827
### Example
2928

3029
```jsx
31-
import {
32-
createInstance,
33-
OptimizelyProvider,
34-
useDecision,
35-
} from '@optimizely/react-sdk';
30+
import { createInstance, OptimizelyProvider, useDecision } from '@optimizely/react-sdk';
3631

3732
const optimizelyClient = createInstance({
3833
sdkKey: 'your-optimizely-sdk-key',
@@ -43,8 +38,8 @@ function MyComponent() {
4338
return (
4439
<React.Fragment>
4540
<SearchComponent algorithm={decision.variables.algorithm} />
46-
{ decision.variationKey === 'relevant_first' && <RelevantFirstList /> }
47-
{ decision.variationKey === 'recent_first' && <RecentFirstList /> }
41+
{decision.variationKey === 'relevant_first' && <RelevantFirstList />}
42+
{decision.variationKey === 'recent_first' && <RecentFirstList />}
4843
</React.Fragment>
4944
);
5045
}
@@ -70,7 +65,8 @@ class App extends React.Component {
7065
npm install @optimizely/react-sdk
7166
```
7267

73-
For **React Native**, installation instruction is bit different. Check out the
68+
For **React Native**, installation instruction is bit different. Check out the
69+
7470
- [Official Installation guide](https://docs.developers.optimizely.com/feature-experimentation/docs/install-sdk-reactnative)
7571
- [Expo React Native Sample App](https://github.com/optimizely/expo-react-native-sdk-sample)
7672

@@ -155,9 +151,9 @@ function MyComponent() {
155151
const [decision, isClientReady, didTimeout] = useDecision('the-flag');
156152
return (
157153
<React.Fragment>
158-
{ isClientReady && <div>The Component</div> }
159-
{ didTimeout && <div>Default Component</div>}
160-
{ /* If client is not ready and time out has not occured yet, do not render anything */ }
154+
{isClientReady && <div>The Component</div>}
155+
{didTimeout && <div>Default Component</div>}
156+
{/* If client is not ready and time out has not occured yet, do not render anything */}
161157
</React.Fragment>
162158
);
163159
}
@@ -277,7 +273,7 @@ class MyComp extends React.Component {
277273
constructor(props) {
278274
super(props);
279275
const { optimizely } = this.props;
280-
const decision = optimizely.decide('feat1');
276+
const decision = optimizely.decide('feat1');
281277

282278
this.state = {
283279
decision.enabled,
@@ -298,9 +294,11 @@ const WrappedMyComponent = withOptimizely(MyComp);
298294
Any component under the `<OptimizelyProvider>` can access the Optimizely `ReactSDKClient` via the `OptimizelyContext` with `useContext`.
299295

300296
_arguments_
297+
301298
- `OptimizelyContext : React.Context<OptimizelyContextInterface>` The Optimizely context initialized in a parent component (or App).
302299

303300
_returns_
301+
304302
- Wrapped object:
305303
- `optimizely : ReactSDKClient` The client object which was passed to the `OptimizelyProvider`
306304
- `isServerSide : boolean` Value that was passed to the `OptimizelyProvider`
@@ -321,34 +319,33 @@ function MyComponent() {
321319
};
322320
return (
323321
<>
324-
{ decision.enabled && <p>My feature is enabled</p> }
325-
{ !decision.enabled && <p>My feature is disabled</p> }
326-
{ decision.variationKey === 'control-variation' && <p>Current Variation</p> }
327-
{ decision.variationKey === 'experimental-variation' && <p>Better Variation</p> }
322+
{decision.enabled && <p>My feature is enabled</p>}
323+
{!decision.enabled && <p>My feature is disabled</p>}
324+
{decision.variationKey === 'control-variation' && <p>Current Variation</p>}
325+
{decision.variationKey === 'experimental-variation' && <p>Better Variation</p>}
328326
<button onClick={onClick}>Sign Up!</button>
329327
</>
330328
);
331329
}
332330
```
333331

334332
### Tracking
333+
335334
Use the built-in `useTrackEvent` hook to access the `track` method of optimizely instance
336335

337336
```jsx
338337
import { useTrackEvent } from '@optimizely/react-sdk';
339338

340339
function SignupButton() {
341-
const [track, clientReady, didTimeout] = useTrackEvent()
340+
const [track, clientReady, didTimeout] = useTrackEvent();
342341

343342
const handleClick = () => {
344-
if(clientReady) {
345-
track('signup-clicked')
343+
if (clientReady) {
344+
track('signup-clicked');
346345
}
347-
}
346+
};
348347

349-
return (
350-
<button onClick={handleClick}>Signup</button>
351-
)
348+
return <button onClick={handleClick}>Signup</button>;
352349
}
353350
```
354351

@@ -411,69 +408,46 @@ To rollout or experiment on a feature by user rather than by random percentage,
411408

412409
## Server Side Rendering
413410

414-
Right now server side rendering is possible with a few caveats.
411+
The React SDK supports server-side rendering (SSR). To generate synchronous decisions during SSR, you must pre-fetch the datafile and pass it to `createInstance`. Using `sdkKey` alone is not supported for SSR because it requires an asynchronous network call.
415412

416-
**Caveats**
413+
### Setup
417414

418-
1. You must download the datafile manually and pass in via the `datafile` option. Can not use `sdkKey` to automatically download.
415+
Fetch the datafile on the server, create an Optimizely instance, and wrap your app with `<OptimizelyProvider>`:
419416

420-
2. Rendering of components must be completely synchronous (this is true for all server side rendering), thus the Optimizely SDK assumes that the optimizely client has been instantiated and fired it's `onReady` event already.
417+
```jsx
418+
import { createInstance, OptimizelyProvider, useDecision } from '@optimizely/react-sdk';
421419

422-
### Setting up `<OptimizelyProvider>`
420+
// Pre-fetched datafile (fetching mechanism depends on your framework)
421+
const optimizelyClient = createInstance({
422+
datafile, // must be provided for SSR
423+
});
423424

424-
Similar to browser side rendering you will need to wrap your app (or portion of the app using Optimizely) in the `<OptimizelyProvider>` component. A new prop
425-
`isServerSide` must be equal to true.
425+
function MyComponent() {
426+
const [decision] = useDecision('flag1');
427+
return decision.enabled ? <p>Feature enabled</p> : <p>Feature disabled</p>;
428+
}
426429

427-
```jsx
428-
<OptimizelyProvider optimizely={optimizely} user={{ id: 'user1' }} isServerSide={true}>
429-
<App />
430-
</OptimizelyProvider>
430+
// Wrap your app with OptimizelyProvider
431+
<OptimizelyProvider optimizely={optimizelyClient} user={{ id: 'user1' }} isServerSide={typeof window === 'undefined'}>
432+
<MyComponent />
433+
</OptimizelyProvider>;
431434
```
432435

433-
All other Optimizely components, such as `<OptimizelyFeature>` and `<OptimizelyExperiment>` can remain the same.
436+
### React Server Components
434437

435-
### Full example
438+
The SDK can also be used directly in React Server Components without `OptimizelyProvider`. See the [Next.js Integration Guide](docs/nextjs-ssr.md#react-server-components) for details.
436439

437-
```jsx
438-
import * as React from 'react';
439-
import * as ReactDOMServer from 'react-dom/server';
440+
### Next.js Integration
440441

441-
import {
442-
createInstance,
443-
OptimizelyProvider,
444-
useDecision,
445-
} from '@optimizely/react-sdk';
442+
For detailed Next.js examples covering both App Router and Pages Router patterns, see the [Next.js Integration Guide](docs/nextjs-ssr.md).
446443

447-
const fetch = require('node-fetch');
444+
### Limitations
448445

449-
function MyComponent() {
450-
const [decision] = useDecision('flag1');
451-
return (
452-
<React.Fragment>
453-
{ decision.enabled && <p>The feature is enabled</p> }
454-
{ !decision.enabled && <p>The feature is not enabled</p> }
455-
{ decision.variationKey === 'variation1' && <p>Variation 1</p> }
456-
{ decision.variationKey === 'variation2' && <p>Variation 2</p> }
457-
</React.Fragment>
458-
);
459-
}
446+
- **Datafile required** — SSR requires a pre-fetched datafile. Using `sdkKey` alone falls back to a failed decision.
447+
- **Static user only** — User `Promise` is not supported during SSR.
448+
- **ODP segments unavailable** — ODP audience segments require async I/O and are not available during server rendering.
460449

461-
async function main() {
462-
const resp = await fetch('https://cdn.optimizely.com/datafiles/<Your-SDK-Key>.json');
463-
const datafile = await resp.json();
464-
const optimizelyClient = createInstance({
465-
datafile,
466-
});
467-
468-
const output = ReactDOMServer.renderToString(
469-
<OptimizelyProvider optimizely={optimizelyClient} user={{ id: 'user1' }} isServerSide={true}>
470-
<MyComponent />
471-
</OptimizelyProvider>
472-
);
473-
console.log('output', output);
474-
}
475-
main();
476-
```
450+
For more details and workarounds, see the [Next.js Integration Guide — Limitations](docs/nextjs-ssr.md#limitations).
477451

478452
## Disabled event dispatcher
479453

0 commit comments

Comments
 (0)