Skip to content

Commit e1d2d30

Browse files
yusintocodyde
andauthored
feat: Added react-universal code. (#493)
Co-authored-by: Cody De Arkland <[email protected]>
1 parent c771ab7 commit e1d2d30

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+767
-63
lines changed

.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module.exports = {
1313
'**/dist/**',
1414
'**/vercel/examples/**',
1515
'**/react-native/example/**',
16+
'**/react-universal/example/**',
1617
'**/fromExternal/**',
1718
],
1819
rules: {
@@ -41,10 +42,10 @@ module.exports = {
4142
'import/no-cycle': 'error',
4243
'import/no-useless-path-segments': 'error',
4344
'import/no-duplicates': 'error',
45+
'import/prefer-default-export': 'off',
4446
'jest/no-disabled-tests': 'warn',
4547
'jest/no-focused-tests': 'error',
4648
'jest/no-identical-title': 'error',
47-
// 'jest/prefer-to-have-length': 'warn',
4849
'jest/valid-expect': 'error',
4950
},
5051
globals: {

.github/workflows/release-please.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,6 @@ jobs:
343343
permissions:
344344
id-token: write
345345
contents: write
346-
# HACK: react-universal sdk is not ready for release yet.
347346
if: false #${{ needs.release-please.outputs.package-react-universal-release == 'true' }}
348347
steps:
349348
- uses: actions/checkout@v4

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"packages/sdk/react-native",
1414
"packages/sdk/react-native/example",
1515
"packages/sdk/react-universal",
16+
"packages/sdk/react-universal/example",
1617
"packages/sdk/vercel",
1718
"packages/sdk/akamai-base",
1819
"packages/sdk/akamai-base/example",

packages/sdk/react-native/example/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ yarn && yarn build
1414
MOBILE_KEY=abcdef12456
1515
```
1616

17-
3. Replace `dev-test-flag` with your flag key in `src/welcome.tsx`.
17+
3. Replace `my-boolean-flag-1` with your flag key in `src/welcome.tsx`.
1818

1919
4. Run the app:
2020

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
example

packages/sdk/react-universal/README.md

Lines changed: 26 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,62 +9,49 @@
99
> [!CAUTION]
1010
> This library is a beta version and should not be considered ready for production use while this message is visible.
1111
12-
> **An idiomatic LaunchDarkly SDK which supports RSC, server side rendering and bootstrapping** :clap:
12+
## Features
1313

14-
This SDK supports:
14+
- Supports both React Server Components and Client Components
15+
- Idiomatic server side rendering
16+
- Bootstrapping out of the box
1517

16-
- React Server Components
17-
- Server side rendering
18-
- Bootstrapping
19-
20-
## Installation
18+
## Install
2119

2220
```shell
2321
# npm
24-
npm i @launchdarkly/react-universal-sdk --save-dev
22+
npm i @launchdarkly/react-universal-sdk
2523

2624
# yarn
2725
yarn add -D @launchdarkly/react-universal-sdk
2826
```
2927

30-
### Server API
28+
## Server API
3129

32-
- `initNodeSdk` - Initializes the Node SDK on server startup using the [instrumentation hook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)
30+
- `initNodeSdk` - Initializes the Node SDK on startup.
3331

34-
- `getBootstrap` - Returns a json suitable for bootstrapping the js sdk.
32+
- `getBootstrap` - Produces suitable bootstrap the js sdk.
3533

36-
- `useLDClientRsc` - Use this to get an ldClient for Server Components.
34+
- `useLDClientRsc` - Gets a suitable ld client for Server Components.
3735

38-
### Client API
36+
## Client API
3937

4038
- `LDProvider` - The react context provider.
4139

42-
- `useLDClient` - Use this to get an ldClient for Client Components.
40+
- `useLDClient` - Gets a suitable ld client for Client Components.
4341

4442
## Usage
4543

46-
1. Enable [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation) in `next.config.mjs`:
44+
1. On server start, initialize the Node Server SDK. If you are using NextJS App Router, do this in `instrumentation.ts`. You'll need to enable the [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation):
4745

4846
```ts
49-
/** @type {import('next').NextConfig} */
50-
const nextConfig = {
51-
experimental: { instrumentationHook: true },
52-
};
53-
54-
export default nextConfig;
55-
```
56-
57-
2. Create a new file `instrumentation.ts` at the root of your project. This will initialize the Node Server SDK.
58-
59-
```ts
60-
import { initNodeSdk } from '@/ld/server';
47+
import { initNodeSdk } from '@launchdarkly/react-universal-sdk/server';
6148

6249
export async function register() {
6350
await initNodeSdk();
6451
}
6552
```
6653

67-
3. In your root layout component, render the `LDProvider` using your `LDContext` and `bootstrap`:
54+
2. At the application root, render the `LDProvider` with your `LDContext` and `bootstrap`. In App Router, do this in the root layout:
6855

6956
```tsx
7057
export default async function RootLayout({
@@ -91,42 +78,38 @@ export default async function RootLayout({
9178
}
9279
```
9380

94-
4. Server Components must use the async `useLDClientRsc` function:
81+
3. Server Components must use the async `useLDClientRsc` function:
9582

9683
```tsx
9784
// You should use your own getLDContext function.
9885
import { getLDContext } from '@/app/utils';
99-
import { useLDClientRsc } from '@/ld/server';
10086

101-
export default async function Page() {
87+
import { useLDClientRsc } from '@launchdarkly/react-universal-sdk/server';
88+
89+
export default async function ServerComponent() {
10290
const ldc = await useLDClientRsc(getLDContext());
103-
const flagValue = ldc.variation('dev-test-flag');
91+
const flagValue = ldc.variation('my-boolean-flag-1');
10492

105-
return (
106-
<main className="flex min-h-screen flex-col items-center justify-between p-24">
107-
Server Component: {flagValue.toString()}
108-
</main>
109-
);
93+
return <>Server Component: {flagValue.toString()}</>;
11094
}
11195
```
11296

113-
5. Client Components must use the `useLDClient` hook:
97+
Client Components must use the `useLDClient` hook:
11498

11599
```tsx
116100
'use client';
117101

118-
import { useLDClient } from '@/ld/client';
102+
import { useLDClient } from '@launchdarkly/react-universal-sdk/client';
119103

120-
export default function LDButton() {
104+
export default function ClientComponent() {
121105
const ldc = useLDClient();
122-
const flagValue = ldc.variation('dev-test-flag');
106+
const flagValue = ldc.variation('my-boolean-flag-1');
123107

124108
return <p>Client Component: {flagValue.toString()}</p>;
125109
}
126110
```
127111

128-
You will see both components are rendered on the server (view source on your browser). However, only Client Components
129-
will respond to live changes.
112+
You will see both Server and Client Components are rendered on the server (view source on your browser). However, only Client Components will respond to live changes because Server Components are excluded from the client bundle.
130113

131114
## Verifying SDK build provenance with the SLSA framework
132115

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
extends: ['plugin:@next/next/recommended'],
3+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Example only - Do not commit
2+
3+
LD_SDK_KEY='<YOUR LD SERVER SDK KEY>'
4+
NEXT_PUBLIC_LD_CLIENT_SIDE_ID='<YOUR LD CLIENT SDK KEY>'
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
.yarn/install-state.gz
8+
9+
# testing
10+
/coverage
11+
12+
# next.js
13+
/.next/
14+
/out/
15+
16+
# production
17+
/build
18+
19+
# misc
20+
.DS_Store
21+
*.pem
22+
23+
# debug
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# local env files
29+
.env*.local
30+
.env
31+
32+
# vercel
33+
.vercel
34+
35+
# typescript
36+
*.tsbuildinfo
37+
next-env.d.ts
38+
.idea
Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,35 @@
1-
# LaunchDarkly Universal SDK example
2-
31
> [!IMPORTANT]
42
> This is an experimental project to demonstrate the use of LaunchDarkly with Next.js App Router.
53
>
64
> This is designed for the App Router. Pages router is not supported.
75
8-
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) using App Router.
6+
This example app uses the LaunchDarkly React Universal SDK. It features:
97

10-
## Quickstart
8+
- Server side rendering with both Server Components and Client Components.
9+
- A Client Component example in [app/components/helloClientComponent.tsx](https://github.com/launchdarkly/js-core/tree/main/packages/sdk/react-universal/example/app/components/helloClientComponent.tsx)
10+
- A Server Component (RSC) example in [app/components/helloServerComponent.tsx](https://github.com/launchdarkly/js-core/tree/main/packages/sdk/react-universal/example/app/components/helloServerComponent.tsx)
11+
- Out of the box bootstrapping.
1112

12-
To run this project:
13+
This is a [Next.js](https://nextjs.org/) project created with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) using App Router.
14+
15+
## Quickstart
1316

14-
1. Create an .env file at repo root.
15-
2. Add your SDK key and client-side ID:
17+
1. Rename `.example.env.local` to `.env.local` and use your LaunchDarkly SDK keys:
1618

1719
```dotenv
18-
LD_SDK_KEY=sdk-***
19-
NEXT_PUBLIC_LD_CLIENT_SIDE_ID=***
20+
LD_SDK_KEY='<YOUR LD SERVER SDK KEY>'
21+
NEXT_PUBLIC_LD_CLIENT_SIDE_ID='<YOUR LD CLIENT SDK KEY>'
2022
```
2123

22-
3. Replace `dev-test-flag` with your own flags in `app.tsx` and `LDButton.tsx`.
23-
4. `yarn && yarn dev`
24+
2. Either create `my-boolean-flag-1` in your LaunchDarkly environment or replace with your own flag in [helloClientComponent.tsx](https://github.com/launchdarkly/js-core/tree/main/packages/sdk/react-universal/example/app/components/helloClientComponent.tsx) and [helloServerComponent.tsx](https://github.com/launchdarkly/js-core/tree/main/packages/sdk/react-universal/example/app/components/helloServerComponent.tsx).
25+
26+
3. Finally:
27+
28+
```bash
29+
npm i && npm run dev
30+
31+
# or
32+
yarn && yarn dev
33+
```
2434

25-
You should see your flag value rendered in the browser.
35+
You will see both Server and Client Components are rendered on the server (view source on your browser). However, only Client Components will respond to live changes because Server Components are excluded from the client bundle.

0 commit comments

Comments
 (0)