# Recipe: global header and footer with the Next.js App Router SDK

> Render a header and a footer that authors manage as separate Uniform compositions on each page, together with the page composition.

Source: https://docs.uniform.app/docs/sdk/nextjs-app-router/recipes/global-header-footer

**Goal:** show the same header and footer on each page. Authors edit the header and the footer one time, in their own compositions. Each page then shows the new version.

## Prerequisites

- A Next.js app with the SDK. Refer to [Next.js App Router SDK](https://docs.uniform.app/docs/sdk/nextjs-app-router).
- The `app/uniform/[code]/page.tsx` route and the `components/resolveComponent.tsx` file.

## Step 1: Make the compositions in Uniform

1. Make a component definition `globalHeader` with a text parameter `siteName` and a slot `navigation`.
2. Make a component definition `globalFooter` with a text parameter `copyright` and a slot `links`.
3. Make a composition of the type `globalHeader`. Set its slug to `global-header`.
4. Make a composition of the type `globalFooter`. Set its slug to `global-footer`.
5. Publish the two compositions.

The two compositions do not need a project map node. The page gets them by their slug.

## Step 2: Make the header and footer components

`components/global-header.tsx`

```tsx
import {
  type ComponentParameter,
  type ComponentProps,
  UniformSlot,
  UniformText,
} from "@uniformdev/next-app-router/component";

type GlobalHeaderParameters = {
  siteName?: ComponentParameter<string>;
};

export const GlobalHeader = ({
  parameters: { siteName },
  slots,
  component,
}: ComponentProps<GlobalHeaderParameters, "navigation">) => (
  <header className="flex items-center justify-between p-4">
    {siteName ? <UniformText component={component} parameter={siteName} as="strong" /> : null}
    <nav className="flex gap-4">
      <UniformSlot slot={slots.navigation} />
    </nav>
  </header>
);
```

`components/global-footer.tsx`

```tsx
import {
  type ComponentParameter,
  type ComponentProps,
  UniformSlot,
  UniformText,
} from "@uniformdev/next-app-router/component";

type GlobalFooterParameters = {
  copyright?: ComponentParameter<string>;
};

export const GlobalFooter = ({
  parameters: { copyright },
  slots,
  component,
}: ComponentProps<GlobalFooterParameters, "links">) => (
  <footer className="p-4">
    <UniformSlot slot={slots.links} />
    {copyright ? <UniformText component={component} parameter={copyright} as="p" /> : null}
  </footer>
);
```

For the link components in the slots, refer to [Links](https://docs.uniform.app/docs/sdk/nextjs-app-router/recipes/links).

## Step 3: Register the components

Add the two components to the component map:

`components/resolveComponent.tsx`

```tsx
import type { ResolveComponentFunction, ResolveComponentResult } from "@uniformdev/next-app-router";
import type { ComponentProps } from "@uniformdev/next-app-router/component";

import { GlobalFooter } from "./global-footer";
import { GlobalHeader } from "./global-header";
import { HeroComponent } from "./hero";
import { PageComponent } from "./page";

const componentMap: Record<string, ResolveComponentResult["component"]> = {
  page: PageComponent,
  hero: HeroComponent,
  globalHeader: GlobalHeader,
  globalFooter: GlobalFooter,
};

const NotFound = ({ type }: ComponentProps) => <div>Component not found: {type}</div>;

export const resolveComponent: ResolveComponentFunction = ({ component }) => ({
  component: componentMap[component.type] ?? NotFound,
});
```

## Step 4: Resolve and render the three compositions

Replace `UniformComposition` in the page. Get the three compositions at the same time with `Promise.all`. Then render them in one `UniformContext`:

`app/uniform/[code]/page.tsx`

```tsx
import {
  requireComposition,
  resolveCompositionBySlug,
  resolveRouteFromCode,
  UniformContext,
  UniformResolvedComposition,
  type UniformPageParameters,
} from "@uniformdev/next-app-router";
import { resolveComponent } from "@/components/resolveComponent";

export default async function UniformPage(props: UniformPageParameters) {
  const { code } = await props.params;

  const [page, header, footer] = await Promise.all([
    resolveRouteFromCode({ code }).then(requireComposition),
    resolveCompositionBySlug({ slug: "global-header", code }),
    resolveCompositionBySlug({ slug: "global-footer", code }),
  ]);

  return (
    <UniformContext result={page}>
      <UniformResolvedComposition result={header} resolveComponent={resolveComponent} />
      <main>
        <UniformResolvedComposition result={page} resolveComponent={resolveComponent} />
      </main>
      <UniformResolvedComposition result={footer} resolveComponent={resolveComponent} />
    </UniformContext>
  );
}
```

If you use Cache Components, import `resolveRouteFromCode` and `resolveCompositionBySlug` from `@uniformdev/next-app-router/cache`. The rest of the code stays the same. Refer to [Caching](https://docs.uniform.app/docs/sdk/nextjs-app-router/caching#cache-components).

## How it works

- **The same page state:** each resolver gets the same `code`. Thus the header and the footer use the state, the locale and the release of the page. In Canvas preview, they show the draft version. Visitors see the published version.
- **A missing composition:** if Uniform has no composition for a slug, the resolver returns `route: undefined`. Then `UniformResolvedComposition` renders nothing, and the page still renders. `requireComposition` is only on the page composition. Thus only a missing page gives a 404.
- **Personalization:** the header and the footer can have personalizations and A/B tests. The SDK renders them the same as in the page composition.

### Why one UniformContext

`UniformContext` does these tasks for the full page:

- It starts the Uniform Context in the browser with the data of one composition. Give it the page composition.
- It adds the visual editing script in draft and editor states.
- In edge mode, it adds the script that the edge middleware fills with the visitor state.

A second `UniformContext` adds these scripts a second time. The browser uses only the first Context, so the second Context does no useful work. Thus render `UniformContext` one time, and put all `UniformResolvedComposition` elements in it. Refer to [Render more than one composition](https://docs.uniform.app/docs/sdk/nextjs-app-router/resolving-compositions#render-more-than-one-composition).

### How the header and footer stay up to date

A published composition that you get by slug has two cache tags:

| Tag | Example |
| --- | --- |
| `composition-slug:<slug>` | `composition-slug:global-header` |
| `composition:<id>` | `composition:6c4f…` |

When an author publishes the header, Uniform sends the `composition.published` webhook. The webhook handler in `app/api/preview/route.ts` revalidates these tags with the `"max"` profile. Next.js then marks the cached pages as stale. The next request to a page gets the old page and starts a new render in the background. The request after it gets the new header. Refer to [Caching](https://docs.uniform.app/docs/sdk/nextjs-app-router/caching#webhooks).

> **Warning:**
>
> Do not put the header in `app/layout.tsx` with its own `UniformContext`. A page then has two `UniformContext` elements. Resolve and render all compositions of the page in the page file.
