# Layouts and the default Layout

Choose which header and footer wrap your pages, set the site's default Layout, and switch them off for one page.

A Layout says which components fill the areas around a page, such as its header and
footer. Layouts live under **Page Builder → Layouts** in the admin. A Layout is
published like a component: its draft can change without the site seeing it until you
publish.

> [!WARNING]
> Layouts are experimental: the fields, the default's rules and the route option
> may still change before they are marked stable. A page route draws Layouts
> once it turns them on; see [Draw pages inside their
> Layout](#draw-pages-inside-their-layout).

## Which Layout a page gets

Each page has two settings:

* **Layout**: the Layout the page asks for. Leave it empty to use the site's default
  Layout, which is what most pages do.
* **No header or footer**: asks for no Layout at all. It always wins, even when the
  page also names a Layout, so a landing page you switched off stays switched off.

The rule for which Layout a page gets is: the switch first, then the Layout the page
names while it is published, then the site's default while it is published, and
otherwise none. So a page naming a Layout that is not published (or was deleted) gets
the site's default, and a page whose default is not usable gets no header or footer.
`effectiveLayoutFor` from `@nextlyhq/plugin-page-builder` answers this for a stored
page; it is experimental.

## The default Layout

Your site always has exactly one default Layout. On a new site the plugin creates a
published Layout called **Default (site)**, with no areas filled in, and makes it the
default. Fill in its areas, or make another Layout the default.

If you already have a Layout called "Default (site)", or one at the address
`default-site`, the plugin never takes it over. It gives its own Layout the next free
name or address instead, such as "Default (site) 2" or `default-site-2`.

### Make another Layout the default

Call the make-default route on a published Layout:

```http
POST /admin/api/plugins/@nextlyhq/plugin-page-builder/layouts/:id/make-default
```

Only a published Layout can be the default. The caller needs three permissions:

* **Manage page builder site settings** (`manage-page-builder-site`),
* **update** on Layouts, and
* **publish** on Layouts.

The default changes every page that names no Layout, so it is treated like publishing.
Send `{ "dryRun": true }` to see what would change without changing it. The answer is
the usual `{ message, item }` shape, where `item` holds `defaultLayout` and
`previousDefaultLayout`.

### The default is protected

You cannot delete or unpublish the default Layout. Make another Layout the default
first. The setting is stored in the `page-builder-settings` Single, and only the
make-default route can change it: a direct write to that Single is refused, even from
the API with access checks turned off.

If the default disappears another way, such as an import that skips hooks, the plugin
repairs it. A background job checks the default every time the jobs runner runs, and
points the default back at the **Default (site)** Layout, creating it again if needed.

## Existing sites: run a migration

This release adds two columns to pages (`layout` and `noHeaderFooter`) and the
`page-builder-settings` Single. A site that already has the page builder installed
gets them only through a migration:

```bash
npx nextly migrate:create add_page_layouts
npx nextly migrate
```

In development you can use `npx nextly db:sync` instead.

## Draw pages inside their Layout

> [!NOTE]
> The `layouts` route option and `pageBuilderLayouts()` are **experimental**:
> their names and shape may change before they are marked stable.

Your site's page route draws the header and footer only when you turn Layouts on for
it. Pass the plugin's provider as the `layouts` option of `createBlocksPage` (or
`createPublicBlocksPage`):

```tsx title="app/(frontend)/[...slug]/page.tsx"
import { createBlocksPage } from "@nextlyhq/blocks-react/next";
import { pageBuilderLayouts } from "@nextlyhq/plugin-page-builder";

const { ContentPage, generateMetadata } = createBlocksPage({
  collections: ["pages"],
  field: "content",
  layouts: pageBuilderLayouts(),
});

export { generateMetadata };
export default ContentPage;
```

Each page is then served in three parts: the header, the page, then the footer. The
header is wrapped in a `<header>` element and the footer in a `<footer>` element,
each with a `data-nextly-region` attribute naming its area, so you can style or test
them. Without the option, the route serves the page exactly as before.

* **Which Layout:** the one the page names while it is published, otherwise the
  site's default, or none when the page has **No header or footer** switched on.
* **No JavaScript added:** the header and footer are drawn on the server, like the
  rest of the page.
* **Titles stay the page's own:** a heading in your header never becomes a page's
  title or description.
* **Updates reach only the pages they affect:** publishing a Layout, or a component
  one uses, refreshes the pages drawn inside it. Changing the default refreshes the
  pages that use the default, including pages whose own Layout is not published.
* **No usable default:** pages that use the default are drawn without a header or
  footer, and the server logs a warning. The plugin's background job repairs the
  default, and those pages pick it up within a minute.

A preview shows the page's own draft choice of Layout. A route that serves drafts
outright (`draft: true`) also shows the Layout's and its components' drafts; a
preview link shows their published versions.

If your site renamed the Layouts collection or the `page-builder-settings` Single,
pass the new names, or the route reads the old ones:
`pageBuilderLayouts({ layoutsCollection: "frames", settingsSingle: "site-settings" })`.
For a route that reads through its own Nextly instance, pass that reader as
`nextly`. The option is not offered on a Single's route.
