You're reading docs for Nextly Alpha. APIs may change between releases.

Page Builder

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.

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.

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:

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:

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

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):

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.