Skip to main content

Documentation

No results found.
Features

Shared Rows

A shared row is a row saved once and reused on multiple pages. Editing it on any page updates every page that uses it — so site-wide CTAs, footer variants, contact blocks, and other repeated sections only have to be maintained in one place....

A shared row is a row saved once and reused on multiple pages. Editing it on any page updates every page that uses it — so site-wide CTAs, footer variants, contact blocks, and other repeated sections only have to be maintained in one place. Shared rows are managed from the Dashboard → Shared Rows page and created from the page editor by toggling any row to "Shared".


The problem

Every site has sections that need to appear on multiple pages with identical content: a "Book a demo" CTA, a small contact block, a promotional banner, a customised footer variant. Without a sharing mechanism, the editor either has to duplicate the row on every page (and remember to update each copy when the content changes) or wire the section into the layout (which makes it global and removes per-page placement control).

Shared rows give editors a third option: place a row anywhere on any page, but keep its content centralised so an edit propagates everywhere automatically.

The fix

Any row on any page can be turned into a shared row from the page editor — the row's contents move into a dedicated views/shared-rows/{slug}.blade.php file, and the original page now references the row via @include instead of holding its content inline. Adding the same shared row to another page reuses the same @include, so both pages render the same source.

Editing a shared row from any page that uses it writes back to the shared file. The next page render — anywhere the row is included — picks up the change.

Creating a shared row

From the page editor, every row card has a "Make shared" action. Clicking it:

  1. Writes the row's blade to resources/views/shared-rows/{slug}.blade.php (the slug uses dashes instead of colons to be filename-safe)
  2. Replaces the row's inline blade in the page file with @include('shared-rows.{filename}')
  3. Inserts a shared_rows row to register the slug + display name
  4. Updates existing ContentOverride rows for the slug, setting page_slug = null so the overrides are page-agnostic going forward

The row's editor card now shows a "Shared" badge and the field values become the source of truth for every page that includes it. Implementation lives in EditorLibraryActions::makeRowShared.

Adding a shared row to another page

The page editor's "Add row" drawer has a Shared tab listing every shared row on the install. Clicking one inserts an @include('shared-rows.{filename}') line into the target page at the chosen position. No content is copied — the new page renders the same shared file directly.

Editing a shared row

A shared row appears in the editor exactly like any other row — the same item cards, the same Content / Design / Advanced tabs, the same field types. The only difference is that saving writes to the shared file, not the host page's blade. Every page that includes the shared row picks up the change on its next render.

Refreshing a shared row from the design library

If a shared row's source design library template has been updated (e.g. a designer pushed new defaults), the shared row can be refreshed from that template. The page editor's per-row refresh action handles shared rows by writing the new template to the shared-rows/{slug}.blade.php file and re-baking active overrides on top.

The page-wide refresh action skips shared rows by default because refreshing one shared row affects every page that includes it. An opt-in checkbox in the refresh modal — "Include shared rows on this page" — lets the user choose to refresh them anyway. Implementation lives in EditorDiscardActions::refreshSharedRowBlade.

The Shared Rows dashboard

Dashboard → Shared Rows lists every shared row in the install with:

  • The display name and slug
  • The number of pages using it (with an expandable list of page names + edit links)
  • An Edit button that opens the editor — either on the first page that uses the row, or in a standalone wrapper file if no page currently includes it
  • A Delete button that removes the row

Deleting a shared row scans every page file for ROW:start:{slug} markers, removes the include line from each affected page, and deletes the shared file plus its shared_rows registry row. The delete confirmation modal lists every page about to be affected so the user sees the blast radius before confirming. Implementation lives in ⚡shared-rows.blade.php.

Override storage and propagation

Field values for a shared row are stored as ContentOverride rows with row_slug = {shared_slug} and page_slug = null — i.e. not scoped to any one page. When any page renders the shared @include, the row hits the same set of overrides, so all pages render the same content.

This is how propagation works without any push step: every page reads from the same DB rows. Editing those rows from one page is identical to editing them from any other page; the next render anywhere reflects the new value.

If a single page needs to diverge on a few fields without losing sync on the rest, use per-page customization. To split a row into entirely separate variants, convert it back to a regular row (or insert a fresh non-shared row from the design library).

Per-page customization

A shared row can be customized for one specific page while keeping every other page in sync with the global content. This is useful when a single landing page needs a different headline or CTA label than the rest of the site, but everything else about the row should still propagate.

In the page editor, a shared row's card shows a paint-brush "Customize for this page only" button alongside its share badge. Clicking it puts that row into per-page override mode on the current page only:

  • The badge changes to an amber "Page only" label so it's obvious at a glance that this instance has diverged from the global shared content.
  • Every field on the row — text, headings, toggles, images, repeaters, and *_classes style fields — saves to a per-page override record instead of the global one.
  • Other pages that include the same shared row are unaffected and continue to read the global values.

Editing the row globally from a different page still works the same way. A page-specific override on Page A always wins for Page A; everywhere else falls back to the global record. Likewise, fields the user did not touch in page-override mode continue to inherit from the global record — only the specific keys the user set on Page A diverge.

A "Reset to shared" action sits next to the page-only badge. Clicking it deletes every per-page override for that row on the current page and clears any per-page class overrides written into the page file, returning the row to its global shared values.

Class fields (anything ending in _classes) are written into a comment block above the row's @include line in the page file so Tailwind's scanner can see them at build time. Resetting clears that block automatically.

When the editor opens a page that already has per-page overrides for a shared row, the row starts in page-override mode automatically — no need to click the customize button again.

What it isn't

  • Shared rows are not partial templates. They render as full editable rows with their own override store. A partial template that needs PHP logic or layout-level placement is still a Blade view in resources/views/.
  • Shared rows don't override the layout's header or footer slots. Site-wide header and footer are managed through the layout / branding system, not the shared rows feature. Shared rows live inside the page body.

Common use cases

Pattern Why share it
Site-wide "Book a demo" CTA Update the headline or button text once, reflect it across every landing page
Footer variants When the footer differs between marketing pages and product pages, share each variant separately
Newsletter signup Centralise a single signup form so subscriber-list changes only happen in one place
Contact block (address, phone, hours) Treat business info as a single source of truth
Promotional banner Roll out a campaign banner across the site, then turn it off in one place when the campaign ends
Standardised testimonials section One canonical testimonials grid reused across product pages

What lives where

Path Purpose
app/Models/SharedRow.php Eloquent model — slug + name registry of every shared row in the install.
resources/views/shared-rows/ The shared blade files. One per shared row, named {slug-with-dashes}.blade.php.
resources/views/pages/dashboard/⚡shared-rows.blade.php The Shared Rows dashboard — list, edit, delete with usage counts.
app/Support/VoltFileService.php makeRowShared, deleteSharedRow, file I/O for shared row blade files.
app/Concerns/EditorLibraryActions.php makeRowShared (convert row to shared), insertSharedRow (add a shared row to a page).
app/Concerns/EditorDiscardActions.php refreshSharedRowBlade and the page-wide opt-in flag for refreshing shared rows.

For the broader page editor that hosts the make-shared and add-shared actions, see page-editor-tour.md. For the catalogue of pre-designed rows that shared rows are typically created from, see design-library.md. For engineers touching the orchestration (cache tiers, save flow, override resolution), see shared-row-architecture.md.