Skip to content

docs: add documentation for the Next.js app router #5265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 77 additions & 8 deletions docs/docs/clients/client-side/nextjs-and-ssr.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ application with the resulting state.
Example applications for a variety of Next.js and SSR can be found
[here](https://github.com/flagsmith/flagsmith-js-examples/tree/main/nextjs).

Example application using the Next.js app router can be found
[here](https://github.com/Flagsmith/flagsmith-js-examples/tree/main/nextjs-approuter).

Example applications for Svelte be found [here](https://github.com/flagsmith/flagsmith-js-examples/tree/main/svelte).

An example application for Next.js middleware can be found
Expand Down Expand Up @@ -43,10 +46,72 @@ The main flow with Next.js and any JavaScript-based SSR can be as follows:

### Example: Initialising the SDK with Next.js

Taking the above into account, the following example fetches flags on the server and initialises Flagsmith with the
state.
Taking the above into account, the following examples fetch flags on the server and initialises Flagsmith with the
state. Below is an example for the **app** router as well as the **pages** router.

#### App Router Example

```javascript
// src/app/components/FeatureFlagProvider.tsx
"use client";

import { ReactNode, useRef } from "react";

import { FlagsmithProvider } from "flagsmith/react";
import { IState } from "flagsmith/types";
import { createFlagsmithInstance } from "flagsmith/isomorphic";

export const FeatureFlagProvider = ({
serverState,
children,
}: {
serverState: IState;
children: ReactNode;
}) => {
const flagsmithInstance = useRef(createFlagsmithInstance());
return (
<FlagsmithProvider flagsmith={flagsmithInstance.current} serverState={serverState}>
<>{children}</>
</FlagsmithProvider>
);
};


// src/app/layout.jsx
import { ReactNode } from "react";
import { FeatureFlagProvider } from './components/FeatureFlagProvider';
import flagsmith from "flagsmith/isomorphic";

export default async function RootLayout({
children,
}: Readonly<{
children: ReactNode;
}>) {
await flagsmith.init({
environmentID: "<YOUR_SERVERSIDE_ENVIRONMENT_KEY>",
// Add optional identity, etc.
});
const serverState = flagsmith.getState();

return (
<html lang="en">
<head>
<meta name="viewport" content="initial-scale=1, width=device-width" />
</head>
<body>
<FeatureFlagProvider serverState={serverState}>
{children}
</FeatureFlagProvider>
</body>
</html>
);
}
```

#### Pages Router Example

```javascript
// src/pages/_app.jsx
import { FlagsmithProvider } from 'flagsmith/react';
import { createFlagsmithInstance } from 'flagsmith/isomorphic';
function MyApp({ Component, pageProps, flagsmithState }) {
Expand All @@ -62,7 +127,7 @@ MyApp.getInitialProps = async () => {
const flagsmithSSR = createFlagsmithInstance();
await flagsmithSSR.init({
// fetches flags on the server
environmentID: '<YOUR_ENVIRONMENT_ID>',
environmentID: '<YOUR_SERVERSIDE_ENVIRONMENT_KEY>',
identity: 'my_user_id', // optionaly specify the identity of the user to get their specific flags
});
return { flagsmithState: flagsmithSSR.getState() };
Expand All @@ -71,7 +136,11 @@ MyApp.getInitialProps = async () => {
export default MyApp;
```

#### Client Component

```javascript
'use client'; // Only required by the app router version.

import { useFlags } from 'flagsmith/react';

export function MyComponent() {
Expand All @@ -85,7 +154,7 @@ export function MyComponent() {
}
```

From that point the SDK usage is the same as the [React SDK Guide](/clients/react)
From this point on, the SDK usage is the same as the [React SDK Guide](/clients/react)

### Example: Flagsmith with Next.js middleware

Expand All @@ -107,7 +176,7 @@ export async function middleware(request: NextRequest) {
}

await flagsmith.init({
environmentID: '<YOUR_ENVIRONMENT_ID>',
environmentID: '<YOUR_SERVERSIDE_ENVIRONMENT_KEY>',
identity,
});

Expand Down Expand Up @@ -135,7 +204,7 @@ Step 1: Initialising the SDK and passing the resulting state to the client.
```javascript
await flagsmith.init({
// fetches flags on the server
environmentID: '<YOUR_ENVIRONMENT_ID>',
environmentID: '<YOUR_SERVERSIDE_ENVIRONMENT_KEY>',
identity: 'my_user_id', // optionaly specify the identity of the user to get their specific flags
});
const state = flagsmith.getState(); // Pass this data to your client
Expand All @@ -144,13 +213,13 @@ const state = flagsmith.getState(); // Pass this data to your client
Step 2: Initialising the SDK on the client.

```javascript
flagsmith.setState(state); // set the state based on your
flagsmith.setState(state);
```

Step 3: Optionally force the client to fetch a fresh set of flags

```javascript
flagsmith.getFlags(); // set the state based on your
flagsmith.getFlags();
```

From that point the SDK usage is the same as the [JavaScript SDK Guide](/clients/javascript)
74 changes: 74 additions & 0 deletions frontend/common/code-help/init/init-next-app-router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Constants from 'common/constants'
module.exports = (
envId,
{ FEATURE_NAME, FEATURE_NAME_ALT, LIB_NAME, NPM_CLIENT },
) => `// app/layout.tsx
import React from "react";
import ${LIB_NAME} from "${NPM_CLIENT}/isomorphic";
import { FeatureFlagProvider } from "./components/FeatureFlagProvider";

export default async function RootLayout({
children,
}: Readonly&lt;{
children: React.ReactNode;
}&gt;) {
await flagsmith.init({
environmentID: "${envId}",${
Constants.isCustomFlagsmithUrl()
? `\n api: "${Constants.getFlagsmithSDKUrl()}",\n`
: ''
} });
const serverState = flagsmith.getState();

return (
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta name="viewport" content="initial-scale=1, width=device-width" /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;FeatureFlagProvider serverState={serverState}&gt;
{children}
&lt;/FeatureFlagProvider&gt;
&lt;/body&gt;
&lt;/html&gt;
);
}

// app/components/FeatureFlagProvider.tsx
"use client";

import { ReactNode, useRef } from "react";
import { FlagsmithProvider } from "flagsmith/react";
import { IState } from "flagsmith/types";
import { createFlagsmithInstance } from "flagsmith/isomorphic";

export const FeatureFlagProvider = ({
serverState,
children,
}: {
serverState: IState;
children: ReactNode;
}) =&gt; {
const flagsmithInstance = useRef(createFlagsmithInstance());

return (
&lt;FlagsmithProvider flagsmith={flagsmithInstance.current} serverState={serverState}>
&lt;&gt;{children}&lt;/&gt;
&lt;/FlagsmithProvider&gt;
);
};

// app/page.tsx
"use client";

import { useFlags } from 'flagsmith/react';

export default function HomePage() {
const flags = useFlags(['${FEATURE_NAME}','${FEATURE_NAME_ALT}']); // only causes re-render if specified flag values / traits change
const ${FEATURE_NAME} = flags.${FEATURE_NAME}.enabled
const ${FEATURE_NAME_ALT} = flags.${FEATURE_NAME_ALT}.value

return (
&lt;&gt;{...}&lt;/&gt;
);
}`
3 changes: 2 additions & 1 deletion frontend/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ const Constants = {
'Go': require('./code-help/init/init-go')(envId, keywords),
'Java': require('./code-help/init/init-java')(envId, keywords),
'JavaScript': require('./code-help/init/init-js')(envId, keywords),
'Next.js': require('./code-help/init/init-next')(envId, keywords),
'Next.js (app router)': require('./code-help/init/init-next-app-router')(envId, keywords),
'Next.js (pages router)': require('./code-help/init/init-next-pages-router')(envId, keywords),
'Node JS': require('./code-help/init/init-node')(envId, keywords),
'PHP': require('./code-help/init/init-php')(envId, keywords),
'Python': require('./code-help/init/init-python')(envId, keywords),
Expand Down