Page settings dialog
Everything about a landing page apart from where it is published: its name, access, search and sharing previews, tracking and forms.
import { PageSettingsDialog } from "@/pages/library/PageSettings";
import "@/pages/library/library.css";PageSettingsDialog holds the settings that belong to the page itself, whether or not it is live. Where the page is published is the publish dialog's job. On a live page, saved settings are held back until the page is published again, like any other change, and the page builder's toast says so.
The dialog is large (800px) and its body scrolls, in six groups: General, Access control, SEO settings, Open Graph settings, Tracking and Forms. The search and share previews update as people type.
A new draft
Every setting starts from its default, filled in by metaOf(page). The search title falls back to the page name, there is no description yet, and the Open Graph image is a snapshot of the page's first two sections.
{settingsOpen && (
<PageSettingsDialog
page={page}
address={live ? pageUrl(page) : `${PETAVUE_DOMAIN}/${page.settings?.slug || slugFor(page.name)}`}
onClose={() => setSettingsOpen(false)}
onSave={(name, meta) => {
setMeta(page.id, name, meta);
setSettingsOpen(false);
toast.success(live ? "Settings saved. Publish the page again to put them live." : "Settings saved.");
}}
/>
)}A page with settings filled in
Password access, a meta description, the page hidden from search, its own Open Graph description, a Tag Manager ID, and forms sent to HubSpot and email.
const page = {
name: "Q4 demo request page",
meta: {
title: "Book a Meridian demo",
description: "See how Meridian forecasts your Q4 pipeline from CRM and ad data, and which deals will slip. …",
sitemap: false,
access: "password",
password: "q4-partners",
ogSameDescription: false,
ogDescription: "A 30-minute demo of Meridian, with your own pipeline.",
gtm: "GTM-5K2QX7P",
forms: "both",
},
sections,
};The groups
| Group | Fields | Notes |
|---|---|---|
| General | Page name | A TextInput. An empty name is saved as the old name. |
| Access control | Public, or Anyone with the password | A full-width .lib-scope--fill switch. Password shows a Password field and a note with a lock icon. |
| SEO settings | Search preview, Title tag, Meta description, Sitemap indexing, Page canonical URL | The title tag's placeholder is the page name. The toggle's hint changes with its state. |
| Open Graph settings | Share preview, title and description (each with "Same as SEO …"), image | The title and description fields are disabled while their checkbox is ticked and show the SEO values. |
| Tracking | Petavue tracking pixel, Google Tag Manager ID | The pixel is on by default. |
| Forms | Send form submissions to | A Dropdown: HubSpot, email to the workspace owner, or both. |
Search preview
.lib-serp draws the result as a search engine would: the title in 16px medium --color-primary-800, the address in 12px green (#0B7A3B), and the description in 12px secondary text. The title is cut at 60 characters and the description at 155, with " …" added. With no description it says "Add a meta description. Without one, search engines pick text from the page."
Share preview
.lib-og draws a link card: a 236px image area, the title in 14px medium, the description cut at 200 characters, and the host in --color-grey-400. The image area shows the uploaded image, or the page's first two sections through ScaledPreview until one is uploaded. An uploaded image is read into a data URL and listed with its name and size in kB, with Replace and Delete.
What onSave gets
onSave(name, meta) is called with the page name (trimmed, or the old name when empty) and every setting:
| Key | Default | Cleaned on save |
|---|---|---|
title | the page name | trimmed |
description | "" | trimmed |
sitemap | true | |
canonical | "" | trimmed, trailing slashes removed |
ogSameTitle, ogSameDescription | true | |
ogTitle, ogDescription | "" | |
ogImage | null | { src, name, kb } once uploaded |
access | "public" | |
password | "" | |
pixel | true | |
gtm | "" | trimmed, uppercased |
forms | "hubspot" |
Nothing is validated. A password page can be saved with an empty password, and any text is accepted as a canonical URL or Tag Manager ID.
Layout
| Part | Style |
|---|---|
| Panel | Dialog at lg, 800px. The body scrolls past min(70vh, 720px). |
| Group headings | 12px uppercase semibold secondary text, with a 12px hint under some, and a --color-grey-100 line between groups. |
| Previews | --color-grey-50 fill, 1px --color-grey-100 border, 8px corners. |
| Notes | .lib-publish__note--icon: 12px text on --color-grey-50 with a 15px icon. |
| Footer | Close and Save at 32px. |
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
page | page object | Required. Reads name, meta and sections (the first two draw the share image). | |
address | string | Required. The address without https://, such as go.meridian.com/q4-demo. Shown in the search preview; its host is shown in the share preview. | |
onSave | (name, meta) => void | Save. Does not close the dialog. | |
onClose | () => void | Escape, scrim click, the close icon and Close. |
address has no fallback. Leaving it out throws, because the dialog calls address.split("/") to find the host.
Accessibility
- The wrapper has
role="dialog",aria-modal="true"andaria-label="Page settings". The visible title is "Q4 demo request page settings". - The access switch is two
aria-pressedbuttons in a group labelled "Restrict access". - The search and share previews are
divs with anaria-label, but no role, so most screen readers ignore the label. They repeat what is in the fields. - The file input for the image is hidden and opened by the Upload button. It has
aria-label="Open Graph image file". - Nothing is focused when the dialog opens. Focus is not kept inside and not returned to "Page settings" on close.
Do and don't
- Do add new page options here, in the group they belong to, and add a row to the publish dialog's "Goes live with" if people should see it before publishing.
- Do keep the previews next to the fields they reflect, so people see the effect while typing.
- Don't add where the page is published here. That is the publish dialog's decision.
- Don't save a live page's settings straight to the live page. They wait for the next publish.
When to use it
Use it for landing pages from the page builder. For settings on other objects, such as a workflow or a dashboard, use a Dialog from @/ui with the same group layout, or a settings page if there are more than a few groups.
Where it is used
| File | What |
|---|---|
src/pages/library/PageBuilderPage.jsx | Opened by "Page settings" in the editor header, and by "Change in page settings" in the publish dialog. Saves through setMeta in usePagesStore.js. |
src/pages/library/PageCode.jsx | Not a caller, but the code view writes these settings into index.html and page.config.json. See Code view. |