Skip to main content

Documentation

No results found.
Features

In-Place Header & Footer Editing

Most page builders force you to open a separate screen to edit your header or footer. WebProCMS lets you edit them in the same editor window you're already using for the page — click any row inside the header or footer in the preview and th...

Most page builders force you to open a separate screen to edit your header or footer. WebProCMS lets you edit them in the same editor window you're already using for the page — click any row inside the header or footer in the preview and the editor swaps into that partial, with the page still rendering live below. When you're done, click "Main page" in the segment dropdown and you're back where you started.


The problem

The header and footer aren't part of any single page — they're layout partials included by every page on the site. Most page builders model them as separate "global" objects that live on their own screens. To change a nav link, swap a logo, or tweak a footer column, the editor has to leave the page they're working on, navigate to a dedicated header/footer screen, make the change, navigate back, and verify the result against the page they originally cared about.

That context switch is friction every time. Worse, the standalone header/footer screens usually preview the partial against a blank canvas, so the editor can't see how the change actually lands in the context of the real page until they navigate back.

The fix

The page editor treats the page body, the header partial, and the footer partial as three segments of the same editing session. A segment dropdown in the sidebar lets the editor jump between them; the preview iframe always renders the full live page (main + header + footer), regardless of which segment is currently being edited. Clicking any header or footer row in the preview drills straight into that partial.

Field edits inside the header or footer write to the partial blade file — so changes appear on every page that uses that header/footer, not just the one currently open. The preview reloads against the same page anchor, so the editor sees the change against the real page they were already working on.

How a click on a header row finds the right partial

The editor needs to know, on every iframe click, whether the clicked row lives in the current page or in the active header/footer partial. It builds a partialRowMap when a page loads:

  1. Resolve the page's active header slug (pageHeader) and footer slug (pageFooter), falling back to the site defaults.
  2. Read each partial file (resources/views/layouts/partials/header-{slug}.blade.php, footer-{slug}.blade.php).
  3. Scan for slug="..." attributes and record row_slug => partial_file_path for every match.

When the user clicks a row in the preview iframe, the editor's selectRowBySlug(slug) function first checks the current page's row list. If the slug isn't there, it looks up partialRowMap[slug] — if it finds a match, it knows the row belongs to a partial and routes the editor there instead.

The segment dropdown and the page anchor

The dropdown in the sidebar offers three options when the editor is anchored on a page:

  • Main page — the page file itself (pages/⚡foo.blade.php)
  • Header sections — the active header partial
  • Footer sections — the active footer partial

Selecting an option wire:navigates to a URL of the shape ?file={partial_path}&pageAnchor={page_path}. The pageAnchor query string param carries the original page path forward so the editor can:

  • Keep offering the same three segments in the dropdown (i.e. the user can hop back to the main page or over to the footer without losing context)
  • Render the preview iframe against the full page URL (so the header/footer is shown in its real surroundings, not on a blank canvas)
  • Compute which header/footer slug the page actually uses, by reading the 'header' => '...' / 'footer' => '...' strings out of the anchor page's PHP block when the editor's current $file is a partial

When the editor isn't anchored on a page (e.g. someone deep-linked straight into a partial), the dropdown collapses and the editor just edits the partial alone.

Drilling into the clicked row after navigation

Routing to a partial is a Livewire SPA navigate, not a full page reload, so by the time the new editor instance has mounted, the iframe click event is long gone. The editor carries the target slug through the URL hash:

?file=layouts/partials/header-bar.blade.php&pageAnchor=pages/⚡home.blade.php#drill=header-logo:Abc123

On mount, the editor checks window.location.hash for a #drill= match, strips it from the URL, and calls selectRowBySlug(slug, true) once Alpine has finished hydrating. The user lands on the partial editor with the clicked row's sidebar card already open — same effect as if they had clicked the row in a standalone partial editor, but the navigation was driven by a preview click on a live page.

data-editor-row on partial sections

For preview click-to-edit to work, every row in the partial needs a data-editor-row="{slug}" attribute on its outer element. Page rows get this attribute via the page editor's row-marker system, but partial rows don't render through that pipeline — they're plain @includes in the layout.

x-dl.section adds data-editor-row="{slug}" on the rendered section element when the request matches the design-library.preview route. So when the page editor's preview iframe loads (which uses that route), every section in the included header and footer partials becomes a hoverable, clickable row — same as page rows.

A follow-up change extends the same treatment to four leaf components — x-dl.logo, x-dl.link, x-dl.nav, x-dl.social — because in some header/footer layouts the outer <section> uses display: contents and has no box of its own to outline. Those components now emit data-editor-field-key on their rendered element so the editor's hover outline + click-to-open-sidebar still works for nav links, logos, and social icons inside a partial.

Editing menu items without leaving the page

Clicking the header nav in the preview drills into the nav component's sidebar card, where the editor can choose which named menu (e.g. Main Navigation) the nav renders. But the menu's actual links — the dropdown of pages, custom URLs, dynamic content, and mega-menu columns — are owned by the standalone Menus screen at /dashboard/menus. Historically the editor had to open that screen in another tab to add or rearrange links, which broke the in-place-editing promise.

A Manage menu items button sits directly under the menu selector on the nav field's sidebar card. Clicking it opens a modal that hosts the same item editor used on the Menus screen, scoped to the menu the editor just selected. Inside the modal the editor can:

  • Add a new link (existing page, custom URL, dynamic source, mega menu)
  • Edit any existing link inline
  • Reorder by drag, indent into a sub-item, outdent back to top level
  • Toggle a link active/inactive
  • Delete a link
  • Translate all links into every configured site language with one click
  • For mega menus: edit columns, links, AI-suggested icons

Each change persists immediately to the navigation.menus setting. The page editor listens for the menu-items-updated event the modal dispatches after every save and reloads the preview iframe so the live header re-renders with the new links. When the editor clicks Done, the modal closes and they're back on the same nav field's sidebar card.

The modal is the exact same Livewire component (<livewire:dashboard.menu-items-editor>) used by the standalone Menus screen. Both contexts share one implementation — the page editor renders it inside a Flux modal keyed by the selected menu slug; the Menus screen renders it inline below its menu picker. Menu cross-cutting concerns (create/rename/delete the menu itself, import, export) stay on the Menus screen since they aren't per-link and don't make sense from inside a single nav field's sidebar.

Top-of-page chrome behaviour

When the editor's current file is a header or footer partial (isLayoutPartial), the sticky top bar that normally shows page-level controls (the page title, the page-meta button, the publish/draft toggle) stays anchored to the page anchor instead of the partial. The partial doesn't have its own title or publish state, so showing a pages/⚡home.blade.php-derived title makes more sense than blanking the bar.

Override storage

Edits to a partial-owned row write to ContentOverride rows scoped to the partial's row slug — not to any one page. Because every page on the site @includes the same partial, every page picks up the new override value on its next render. This is the same propagation model as shared rows (page_slug = null), but applied automatically because the row already lives in a layout file rather than a page file.

What lives where

Path Purpose
resources/views/pages/dashboard/pages/⚡editor.blade.php Hosts pageAnchor, partialRowMap, activeSegment(), segmentFiles(), the dropdown UI, and the #drill= hash handler.
resources/views/components/dl/section.blade.php Adds data-editor-row="{slug}" when rendering inside the design-library.preview route so partial sections are clickable in the preview iframe.
resources/views/components/dl/ logo, link, nav, social Emit data-editor-field-key so leaf nav elements inside partials are hoverable even when the outer section is display: contents.
resources/views/layouts/partials/ The header and footer partial blade files, one per installed template.
app/Livewire/Dashboard/MenuItemsEditor.php + view Per-menu items editor (add/edit/reorder/translate/mega columns/icon picker). Mounted both on the Menus screen and in the page editor's nav-field "Manage menu items" modal.
resources/views/pages/dashboard/pages/partials/content-field-alpine.blade.php "Manage menu items" button under the _menu field selector; dispatches request-open-menu-items-editor to the page editor.

Notes

  • The segment dropdown only offers Header / Footer options when the resolved partial file actually exists. A page whose pageHeader is 'none' won't show a header segment option.
  • The pageAnchor is preserved across all three segments — the editor can hop main → header → footer → main without ever leaving the same page context.
  • Switching segments uses wire:navigate, so the editor instance is rebuilt cleanly each time (no stale per-row drafts carry across).
  • Header / footer changes don't need to be re-saved per page — the partial blade and its ContentOverride rows are the single source of truth, so every page picks up the change immediately.
  • The footer is required to include "Powered by WebProCMS" by repo convention; that text lives in the partial and is editable in place like any other footer row.

Marketing angle

Editing your header or footer doesn't have to mean leaving the page you're working on. WebProCMS keeps you in the same editor window — click on a logo, a nav link, or a footer column right in the preview and the sidebar swaps over to that partial automatically. Need to add a new link to the main navigation, reorder a dropdown, or build out a mega menu? Manage menu items does it right there from the nav field's sidebar — no second tab, no separate screen. When you're done, the segment dropdown takes you back to the page. The change is live across your entire site immediately, and you got there without losing your place.