https://github.com/tanben/sample-nextjs
This project showcases a robust integration of LaunchDarkly feature flags within a Next.js application, leveraging the App Router architecture. It demonstrates best practices for implementing LaunchDarkly in both Client and Server components, while exploring various rendering strategies to optimize performance and user experience.
https://github.com/tanben/sample-nextjs
app-router launchdarkly nextjs nextjs13 reactjs
Last synced: 6 months ago
JSON representation
This project showcases a robust integration of LaunchDarkly feature flags within a Next.js application, leveraging the App Router architecture. It demonstrates best practices for implementing LaunchDarkly in both Client and Server components, while exploring various rendering strategies to optimize performance and user experience.
- Host: GitHub
- URL: https://github.com/tanben/sample-nextjs
- Owner: tanben
- License: apache-2.0
- Created: 2023-06-13T14:41:17.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2025-08-27T22:46:48.000Z (10 months ago)
- Last Synced: 2025-08-28T00:14:06.501Z (10 months ago)
- Topics: app-router, launchdarkly, nextjs, nextjs13, reactjs
- Language: JavaScript
- Homepage:
- Size: 5.93 MB
- Stars: 22
- Watchers: 2
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# LaunchDarkly Next.js App Router Demo
This is a sample LaunchDarkly implementation on Next.js using the App Router with Client and Server components and various rendering strategies. It demonstrates how to integrate LaunchDarkly feature flags into a Next.js application.
*Rendering Performance: Server vs Client components*
## Integrating LaunchDarkly React SDK in Next.js
### Challenges and Solutions (Updated for Next.js 15 & React 19)
If you're developing with client components in Next.js, it's crucial to have a good grasp of how the platform manages client-side prerendering and the impact it may have on browser-specific libraries such as the LaunchDarkly React web SDK. For more information on this topic, I suggest checking out this article titled ["Why do Client Components get SSR'd to HTML?"](https://github.com/reactwg/server-components/discussions/4).
Next.js improves page load times by prerendering **both Client and Server components** on the server and sending prerendered HTML to the browser. This makes page load times faster, especially for pages with lots of content and complex JavaScript.
During server-side prerendering, Client components do not involve hydration, which can cause runtime errors when browser-specific APIs are used such as when calling LaunchDarkly React SDK `asyncWithLDProvider`.
**Important Changes in Next.js 15 & React 19:**
- The `use()` hook now requires **stable/cached promises** and cannot be used with promises created during render.
- Server Components cannot use `ssr: false` with `next/dynamic`. Read [Invalid Usage of `suspense` Option of `next/dynamic`](https://nextjs.org/docs/messages/invalid-dynamic-suspense) for details.
- New error: "A component was suspended by an uncached promise"
To avoid these issues, consider implementing the following **updated approach**:
1. Initialize the LaunchDarkly React SDK in a Client component using traditional async patterns.
2. Use code-splitting to defer the loading of the React web SDK until page hydration phase.
3. **Avoid using `use()` hook with promises created in render** - use `useEffect` and `useState` instead for compatibility. Read [React v19 use() does not support promises created in render](https://react.dev/blog/2024/12/05/react-19#use-does-not-support-promises-created-in-render) for details.
4. When using `dynamic()` with `ssr: false`, ensure the component calling it is a **Client Component** (has `"use client"` directive). Read [Skipping SSR](https://nextjs.org/docs/app/guides/lazy-loading#skipping-ssr) for details.
Here is an example of how to incorporate these modifications:
Client component: `components/AsyncWithLDProvider.js`
```
"use client";
import { useEffect, useState } from "react";
import { asyncWithLDProvider, basicLogger } from "launchdarkly-react-client-sdk";
const defaultContext = {
kind: "user",
key: "user-key-123abc",
name: "Sandy Smith",
};
const createLDConfig = (clientSideID, context) => ({
clientSideID,
context,
timeout: 5,
options: {
logger: basicLogger({
destination: (line) => console.log(line),
level: "debug",
}),
},
reactOptions: {
useCamelCaseFlagKeys: true,
},
});
export default function AsyncLDProvider({
children,
context = defaultContext,
clientSideID,
}) {
const [LDProvider, setLDProvider] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!clientSideID) {
setError(new Error("clientSideID is required"));
setLoading(false);
return;
}
let mounted = true;
const initializeLDProvider = async () => {
try {
const config = createLDConfig(clientSideID, context);
const provider = await asyncWithLDProvider(config);
if (mounted) {
setLDProvider(() => provider);
setLoading(false);
}
} catch (err) {
if (mounted) {
setError(err);
setLoading(false);
}
}
};
initializeLDProvider();
return () => {
mounted = false;
};
}, [clientSideID, context]);
if (error) {
return
Error: {error.message};
}
if (loading || !LDProvider) {
return
Loading LaunchDarkly...;
}
return {children};
}
```
create a client wrapper component : `components/ClientAsyncLDProvider.js`:
```
"use client";
import dynamic from "next/dynamic";
const LDAsyncPovider = dynamic(
() => import("@/components/AsyncWithLDProvider"),
{
ssr: false,
loading: () =>
Loading LaunchDarkly...
}
);
export default function ClientAsyncLDProvider({ children, clientSideID }) {
return (
{children}
);
}
```
Then use it in `app/layout.js` (Server Component):
```
import ClientAsyncLDProvider from "@/components/ClientAsyncLDProvider";
export default function Layout({ children }) {
return (
{children}
);
}
```
## Prerequisites
- LaunchDarkly account
- LaunchDarkly React Web client SDK >= 3.8.x
- LaunchDarkly Node.js server SDK >= 9.10.x
- Node.js version >= 18.17.x
- Next.js version 15.x
- React version 19.x
>This [Next.js](https://nextjs.org/) project was bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Setup
1. Create a feature flag in your LaunchDarkly account with the following details:
| Flag Key | Flag Type | Description |
| ------------- | --------- | ------------------- |
| simple-toggle | boolean | Toggles flag status |
2. Clone this repository and navigate to the project directory.
3. Install the required packages by running:
```
npm install
```
4. Create a `.env` file in the project root and add the following environment variables:
```
LAUNCHDARKLY_SDK_KEY="` with your LaunchDarkly server-side SDK key and `` with your LaunchDarkly client-side ID.
## Running the Demo
1. Start the development server:
```
npm run dev
```
> Note: If you encounter a Server 500 error while running the app, as discussed in [vercel/next.js#49677](https://github.com/vercel/next.js/issues/49677), switch to Node.js version 16.8.
2. Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## Learn More
For more information check out the following resources:
- [LaunchDarkly React Web SDK](https://docs.launchdarkly.com/sdk/client-side/react/react-web) - how to get started with the client-side React Web SDK, and links to reference information on all of the supported features.
- [LaunchDarkly Quick Start Guide](https://docs.launchdarkly.com/home/getting-started) - describes how to get started with LaunchDarkly
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.