# Next.js application with app router

> This tutorial describes setting up a Uniform-powered composable website in Next.js and Typescript using the Next.js app router.

Source: https://docs.uniform.app/docs/learn/headless-cms-tutorials/nextjs-app-router

> **Specific SDK version notice:**
>
> This tutorial is updated for the GA version of the new Next.js SDK and requires you to use a version `19.76.0` or higher of the `@uniformdev/canvas-next-rsc` package. If you are using earlier preview versions of the SDK, please contact us so we can help you upgrade your app.

## Quick start

This tutorial was created to help you add Uniform SDK into an existing Next.js app with App Router and help you understand key mechanics. If you'd like to get started more quickly, consider starting with any of these starters below which are pre-wired with the Uniform SDK and ready for use as a starting point or reference for learning.

1. **Hello World starter**

   This is a single component / single page starter with minimal setup. It could be a good starting point for a new project if you plan to use your own component library, or you don't want to start with Uniform's Component Starter Kit.

   [Open sourced here](https://github.com/uniformdev/examples/tree/main/examples/nextjs-app-router). Check the README file to learn how to get started.
2. **Component Starter Kit**

   If you'd like a more feature-rich starting point with 30+ components and a more opinionated structure, the Component Starter Kit is highly recommended ([demo](https://components.uniform.app/)). It's already pre-wired with the latest Uniform SDK for App Router.

   [Open sourced here](https://github.com/uniformdev/component-starter-kit-next-approuter). Check the README file to learn how to get started.
3. **Next.js Commerce**

   If none of the options above is what you are looking for and your scenario could start with [Next.js Commerce](https://nextjscommerceuniform.vercel.app/), then consider our adapted version of Next.js Commerce.

   [Open sourced here](https://github.com/uniformdev/next-commerce). Check the README file to learn how to get started.

> **Use an AI coding assistant:**
>
> The [`uniform-nextjs-app-router`](https://github.com/uniformdev/agent-skills/tree/main/skills/uniform-nextjs-app-router) agent skill teaches coding assistants such as Claude Code, Codex, Cursor, and Copilot how to add Uniform to a Next.js App Router project: middleware, the composition route, component mapping, preview, personalization, and caching. To install it, see [Uniform agent skills](https://docs.uniform.app/docs/guides/ai/agent-skills).

## Video tutorial

If you prefer a video format, here is the recent launch live stream the team did to walk through the process of adding Next.js SDK into Next.js Commerce app:

Video: [Next.js Commerce + Uniform Visual Workspace](https://www.youtube.com/watch?v=4IWDwcecIxg)

## Detailed tutorial

This tutorial describes setting up a Uniform-powered composable website using Next.js 14 App Router, React Server Components, and Typescript. At the end of this tutorial, you'll know how to:

> **Server-side rendering by default:**
>
> The Uniform SDK for Next.js App Router supports both server-side rendering (SSR) and static site generation (SSG) modes. This tutorial will use the SSR mode as the default and calls out the parts where you need to make changes if you want to use SSG.

## 1. Create a Next.js project

Choose a place on your local machine and in that location run the following command to create a Next.js project:

```shell
npx create-next-app@latest
```

Follow the prompts and select the following options:

| Prompt | Response |
| --- | --- |
| Project name? | getting-started |
| Would you like to use Typescript? | Yes |
| Would you like to use ESLint with this project? | Yes |
| Would you like to use Tailwind CSS with this project? | Yes |
| Would you like to use the `src/ directory` with this project? | No |
| Use `App Router` (recommended)? | Yes |
| Would you like to customize the default `import alias`? | No |

## 2. Add the Uniform SDK into your app

This section guides you through the one-time activation of the Uniform SDK.

### 2.1 Install dependency package

1. Navigate to your codebase

   ```shell
   cd <your project name>
   ```
2. Install the Uniform dependencies:

   ```shell
   npm install @uniformdev/canvas-next-rsc
   ```
3. Now create your Uniform configuration file (filename: `uniform.server.config.js`) in the root of your app. This will allow you to adjust some default settings, such as whether the user has granted consent for tracking and cache behavior.

   `uniform.server.config.js`

   ```js
   /** @type {import('@uniformdev/canvas-next-rsc/config').UniformServerConfig} */
   module.exports = {
     defaultConsent: true,
   };
   ```

   [Read more about possible configuration options](https://docs.uniform.app/docs/learn/tutorials/nextjs-app-router/uniform-server-config)

### 2.2 Configure the preview handler

The preview handler is used for visual editing while using Canvas on both the composition and the  pattern level.

The `POST` handler is used for Webhooks, allowing to invalidate external CDN cache based on changes in your Uniform project.

Add a new file with the following snippet:

`app/api/preview/route.ts`

```typescript
    import {
      createPreviewGETRouteHandler,
      createPreviewPOSTRouteHandler,
      createPreviewOPTIONSRouteHandler,
    } from '@uniformdev/canvas-next-rsc/handler';

    export const GET = createPreviewGETRouteHandler({
      playgroundPath: '/playground'
    });
    export const POST = createPreviewPOSTRouteHandler();
    export const OPTIONS = createPreviewOPTIONSRouteHandler();
```

### 2.3. Add component resolver and playground

You now need to configure Canvas components to resolve to React components. This is a basic boilerplate for component resolution. You can adjust this logic as you see fit.

`canvas/index.ts`

```typescript
    import { 
      type ResolveComponentFunction, 
      type ComponentProps, 
      DefaultNotImplementedComponent 
    } from '@uniformdev/canvas-next-rsc/component';

    export const resolveComponent: ResolveComponentFunction = ({
      component
    }) => {
      let componentType: React.ComponentType<ComponentProps<any>> | null = DefaultNotImplementedComponent;

      // todo: add logic to resolve components here
      return {
        component: componentType,
      }
    }
```

Configure a playground route, this will allow you to preview patterns:

`app/playground/page.tsx`

```typescript
    import { UniformPlayground, UniformPlaygroundProps } from '@uniformdev/canvas-next-rsc';
    import { resolveComponent } from '../../canvas';

    export default function PlaygroundPage(props: {
      searchParams: UniformPlaygroundProps['searchParams']
    }) {
      return (
        <UniformPlayground
          {...props}
          resolveComponent={resolveComponent}
        />
      )
    }
```

### 2.4. Add Uniform React SDK components

1. Open your existing layout file and wrap it with `<UniformContext />` component, this will activate key Uniform SDK functionality for `<UniformComposition />` and `<UniformSlot />` components.

   `app/layout.tsx`

   ```typescript
   import { UniformContext } from '@uniformdev/canvas-next-rsc';

   export default function RootLayout({
     children,
   }: {
     children: React.ReactNode
   }) {
     return (
       <html lang="en">
         <body>
           <UniformContext>
             {children}
           </UniformContext>
         </body>
       </html>
     )
   }
   ```
2. Delete the existing `page.tsx` in the root of your `app` folder and create the path below. This is a catch all route which will resolve from path to project map. This example uses `mode="server"` which supports SSR with both nodejs and edge runtimes. If you are building for the static / SSG mode, use `mode="static"`.

   `app/[[...path]]/page.tsx`

   ```typescript
   import { PageParameters, retrieveRoute, UniformComposition } from '@uniformdev/canvas-next-rsc';
   import { resolveComponent } from '../../canvas';

   // Uncomment to statically render routes at build time
   // export { generateStaticParams } from '@uniformdev/canvas-next-rsc';

   export default async function Home(props: PageParameters) {
     const route = await retrieveRoute(props);
     return (
       <UniformComposition
         {...props}
         resolveComponent={resolveComponent}
         route={route}
         // this is the setting for SSR and Edge-side rendering
         // for the static mode (SSG) use mode="static"
         mode="server"
       />
     );
   }
   ```

   > **Incremental activation possible:**
   >
   > In this particular example, the `app/[[...path]]/page.tsx` will instruct that all routes need to be handled by Uniform. If you'd like to activate Uniform only on certain routes, you can choose to do so by adding the code above inside the specific `page.tsx` app route handler instead in `app/page.tsx` for example, if you want Uniform to be activated only on the home page.

## 3. Connect your app to your Uniform project

Open a browser and follow these steps to configure your Uniform project and create your first composition.

Add a new project on _For the US region:_

_For the EU region:_

.

> **Note:**
>
>

You must have access to a Uniform team with the ability to create new projects. If you are an active customer or a partner, please get in touch with your Uniform team administrator colleague and ask for an invite.

If you are not sure or your company does not have a Uniform team, you can request a sandbox [here](https://uniform.dev/try?utm_source=docs).

Start from your _For the US region:_

_For the EU region:_

.

1. Navigate to **Security > API keys**.
2. Click **Add API key**.
3. Enter the following values:

   | Name | Project | Roles |
   | --- | --- | --- |
   | `Getting started key` | `[your project's name]` (it may be auto populated with My first project) | **Viewer** for read-only API key (recommended for production) and **Developer** (if you would like to write to your project from Uniform CLI) |
4. Click **Create API key**.

   ![Create API key](https://docs.uniform.app/images/start-coding/next-js-application/create-api-key.png)
5. Copy the generated API key and project ID using **Copy as .env** under the  to place the values on your clipboard.

   ![How to access the 'Copy as .env' control](https://docs.uniform.app/images/start-coding/next-js-application/create-api-key.png)
6. In the root of your Next.js project, create a `.env` file to hold all environment variables and secrets. Paste the Uniform API Key and Project ID from clipboard:

_For the US region:_

```shell
UNIFORM_API_KEY=[!!! YOUR API KEY !!!]
UNIFORM_PROJECT_ID=[!!! YOUR PROJECT ID !!!]
UNIFORM_PREVIEW_SECRET=your-preview-secret
```

_For the EU region:_

```shell
UNIFORM_API_KEY=[!!! YOUR API KEY !!!]
UNIFORM_PROJECT_ID=[!!! YOUR PROJECT ID !!!]
UNIFORM_PREVIEW_SECRET=your-preview-secret
UNIFORM_CLI_BASE_EDGE_URL=https://eu.uniform.global
UNIFORM_CLI_BASE_URL=https://eu.uniform.app
```

> Add the `.env` file to the `.gitignore` file, excluding it from version control.

### 3.1 Configure Preview URL

In order for Uniform Canvas to know where to go to render preview, you must setup the Canvas preview in your project settings:

![Canvas preview settings](https://docs.uniform.app/images/learn/tutorials/rsc/canvas-preview.png)

This can be either your localhost:3000 endpoint, or a deployed or hosted app. The value of the `secret` query string must match the value of your `UNIFORM_PREVIEW_SECRET` setting set in your app's `.env` file earlier.

### 3.2 Configure webhooks

You may be wondering why you need webhooks if you setup a server-side rendered application. When Uniform Canvas content is fetched during server-side rendering, it's cached at the CDN level that renders the page for best performance. This cache needs to be updated when compositions change and published. Other use cases include changes to the manifest that contains personalization and A/B testing configuration and redirect management. When these entities change, the associated cache has to be invalidated.

This is where webhooks apply.

To set this up, from within your project go to **Settings > Webhooks** and create a new endpoint with the same URL as you used for your preview URL earlier:

```
https://yourapp.vercel.app/api/preview?secret=your-value-from-UNIFORM_PREVIEW_SECRET
```

Make sure all 12 events are selected and click `Create` to complete webhook setup:

![Webhook setup](https://docs.uniform.app/images/learn/tutorials/rsc/webhooks.png)

> **Cache clear timing:**
>
> Typically it takes approximately 5 seconds for the CDN cache to be cleared after a composition is published.

## 4. Create and register components

Uniform introduces the concepts of **[components](https://docs.uniform.app/docs/guides/composition/glossary#component)** and **[compositions](https://docs.uniform.app/docs/guides/composition/glossary#composition)**. Components are building blocks of content on your website. They match your design system and are similar to components in React.js.

Compositions are a structured collection of components representing a portion of your website, in this case, a page. After creating a component, you'll use it in a composition and then render the composition on the front end.

![Components within a composition](https://docs.uniform.app/images/start-coding/next-js-application/components-compositions.png)

### 4.1 Create your first page composition

In the Uniform application, you'll create components to match those in your front-end application, except for the layout component. This is a reversible consideration and assumes you don't want users to modify the layout in Uniform.

1. From your team dashboard, select a project and go to **Experience > Components**.
2. Click either **Create a component** (if this is your first component) or **Add component**.
3. Enter the following values:

   | Field | Value |
   | --- | --- |
   | Component name | `Page` |
   | Public ID | `page` (this is auto generated based on the value in "component name") |
   | `Allow creating new Compositions from this component` checkbox | Checked |
4. Add a `text` parameter called **Title**. Keep the `public id` of the parameters lowercase as suggested.

   > Composition parameters are typically used to contain page meta data (page title and other SEO settings).
5. Create a new slot in `Slots` tab named `Content` and keep `public id` as `content`, lower cased as suggested. Keep the default slot settings as default (`All components and patterns` enabled).

   ![Content slot.](https://docs.uniform.app/images/learn/tutorials/rsc/content-slot.png)
6. **Save and close**.

### 4.2 Model Uniform components

Now you'll create a component that will be placed inside the Page composition. Since most websites have some sort of "Hero" component as the first one on the page, you'll create that.

1. From your team dashboard, select a project and go to **Experience > Components**.
2. Click either **Create a Component** (if this is your first component) or **Add component**.
3. Enter the following values:

   | Field | Value |
   | --- | --- |
   | Component name | `Hero` |
   | Public ID | `hero` (this is auto generated based on the value in "component name") |
   | `Allow creating new Compositions from this component` checkbox | Unchecked |
4. Add a `text` parameter called **Title**. Keep the `public id` of the parameters lowercase as suggested.
5. Add a `text` parameter with multi-line option called **Description**. Keep the `public id` of the parameters lowercase as suggested.

   > In Uniform, component **[parameters](https://docs.uniform.app/docs/guides/composition/glossary#parameter)** are containers for content attributes. These map directly to your React component props.
6. **Save and close** the component.

### 4.3. Create a Home node in your project map

The project map will connect the route to the Page composition you just created.

1. Navigate to project maps in the Uniform app.
2. Create a project map if there isn't yet one for your project.
3. Click  on the root node and select **edit.**
4. Select **Composition** under **Node type** and save the configuration. You will see an error that you have to connect a composition.

![An error is displayed if you don't connect the node to a composition.](https://docs.uniform.app/images/learn/tutorials/rsc/connect-composition-to-node.png)

### 4.4. Implement your first components

You created two components earlier in Uniform. Now you'll add representative implementation for those components in your Next.js codebase, starting with the `Hero` component.

1. Create a new `hero.tsx` component:

   `components/hero.tsx`

   ```tsx
    import { ComponentProps, UniformText } from "@uniformdev/canvas-next-rsc";

    type HeroProps = ComponentProps<{
      title: string,
      description: string
    }>;

    export function Hero({ context, component }: PageProps) {
      return (
        <>
          <UniformText
            context={context}
            component={component}
            parameterId="title"
            as="h1" />

          <UniformText
            context={context}
            component={component}
            parameterId="description"
            as="div" />
        </>
      )
    }
   ```

   > **In-line editing of parameters:**
   >
   > There are two ways to render parameter value with Uniform. The approach above is using a  `UniformText` component helper from Uniform SDK, allowing text parameters to be inline editable by authors within Uniform. If that's not desired, you can render the parameter value raw by simply rendering it as a value:
   > `<div>Parameter value: {title}</div>`
2. Now do the same for the `Page` composition component.
   Create a new `page.tsx` component that will serve as the implementation of the Page composition. Since the `Page` composition contains a `Slot` with public id `content`, you need to add it to the implementation, as well as render the page title. The code below will render the `Hero` component dynamically based on the placement rules in Uniform compositions, as well as any other future components you add to Uniform.

   > Compositions typically contain one more slots. For the sake of simplicity, continue with a single slot setup.

   ```typescript
   import { ComponentProps, UniformText, UniformSlot } from "@uniformdev/canvas-next-rsc";

   type SlotNames = 'content';
   type PageProps = ComponentProps<{
     title: string
   }, SlotNames>;

   export function Page({
     context,
     component,
     slots,
   }: PageProps) {
     return (
       <>
         <div>Page title: {title}</div>
         <div>
           <h2>Components:</h2>
           <UniformSlot
             context={context}
             data={component}
             // this must correspond to the `public id` of the slot created earlier 
             slot={slots.content}
           />
         </div>
       </>
     )
   }
   ```
3. Now, you will map this component by adding the following code to your `canvas/index.ts` file.

   `canvas/index.ts`

   ```typescript
   import { type ResolveComponentFunction, type ComponentProps, DefaultNotImplementedComponent } from '@uniformdev/canvas-next-rsc';
   // add two component imports
   import { Page } from '@/components/Page';
   import { Hero } from '@/components/Hero';

   export const resolveComponent: ResolveComponentFunction = ({
     component
   }) => {
     let componentType: React.ComponentType<ComponentProps<any>> | null = DefaultNotImplementedComponent;

     // add page component resolution 
     if (component.type === 'page') {
       componentType = Page;
     }
     // add hero component resolution
     if (component.type === 'hero') {
       componentType = Hero;
     }

     return {
       component: componentType,
     }
   }
   ```

### 4.5 Implement more components (optional)

Follow steps 4.2 and 4.4 to create new components that aren't composition type and repeat this for any new component you need to bring into Uniform.

## 5. Enable edge runtime (optional)

By default, Next.js will use the nodejs runtime, and the app will render with a serverless function.

If you'd like to take advantage of the edge runtime and render the pages from the CDN nodes closer to visitors and rely on global scale of this process, it's easy to enable this mode on your app route handler level by exporting a specific constant, like it's shown below:

`app/[[...path]]/page.tsx`

```typescript
export const runtime = 'edge';
```

On some CDNs, edge runtime delivery typically renders a page within 100ms or faster from most global locations, which is nearing the performance of static site delivery. Your mileage can vary and there are many factors contributing to performance in this area. Uniform Mesh layer ensures all the internal and external content is cached as aggressively as possible, which is the key to reaching such performance characteristics.

> **Learn more about runtimes:**
>
> It's important to understand the pros and cons of different runtimes. Please consult the official Next.js docs for [edge and Node.js runtimes](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes).

## Working with Uniform Slots

Slots allow rendering components dynamically based on composition content in Uniform, which allows building new pages and changing the layout of existing pages without developer involvement without code changes.

Depending on a specific scenario, there are multiple ways to work with slots in Uniform.

### Simplest way to render a slot

Place a `<UniformSlot />` React component imported from `@uniformdev/canvas-next-rsc` and add `context`, `data` (which component to render) and `slot` (specific slot to render) props into it from the parent component.

```tsx
import { ComponentProps, UniformSlot } from "@uniformdev/canvas-next-rsc";

// "components" is a public id of the slot defined on the Page composition component
type SlotNames = 'components';
type PageProps = ComponentProps<{ title: string }, SlotNames>;

export function Page({ context, component, slots }: PageProps) {
  return (
    <UniformSlot context={context} data={component} slot={slots.components} />
  )
}
```

### Wrap a slot with additional markup

This approach may be needed in case of using React components that require special container markup for inner components, or special hierarchy of DOM nodes. Typically, this is needed for Carousel components.

```tsx
import { ComponentProps, UniformSlot } from "@uniformdev/canvas-next-rsc";

// "components" is a public id of the slot defined on the Page composition component
type SlotNames = 'components';
type PageProps = ComponentProps<{ title: string }, SlotNames>;

export function Page({ context, component, slots }: PageProps) {
  return (
    <UniformSlot context={context} data={component} slot={slots.components}>
        {({ child, component, key }) => {
          return (
            <div key={key} style={{ color: 'red' }}>
              <h3>{component.type}</h3>
              {child}
            </div>
          )
        }}
    </UniformSlot>
  )
}
```

## Temporary limitations

1. **Preview** functionality

   1. The Preview mode isn't supported in the static mode yet.

      **Workaround**: deploy a preview environment in server mode.
   2. Empty slot placeholders are not displaying.
2. **Pattern Editor**: users must save the pattern before it can be previewed, otherwise you will get a 404 error after creating a new pattern.
