Skip to Content

Layouts

What are Layouts?

A Layout is a React component that defines the structural shell of a page — where the header, footer, and content zones (placeholders) are positioned. Every page rendered by the DSS Base Template is wrapped in a layout resolved from the Visor layout configuration.

Layouts are stored in components/layouts/platform-layouts/ for platform-provided layouts, and can be extended with site-specific layouts.


How Layout Resolution Works

When a page loads, the following chain executes:

  1. page.base.tsx fetches the page data and resolves the layout configuration from Visor.
  2. The layout name from the configuration is passed to components/Layout.tsx.
  3. Layout.tsx uses generateFileCollection() to scan all layout components and matches the layout name to a component filename (without extension).
  4. The matched layout component is rendered, receiving the placeholder data.
  5. If no match is found, EmptyBlockLayout is used as the fallback.

The layout name in Visor’s configuration must exactly match the filename of the layout component (without the .tsx extension).


Platform Layouts

The following layouts are included out of the box:

LayoutFileDescription
BaseLayoutBaseLayout.tsxMinimal container wrapper used as a base for other layouts.
HomepageLayoutHomepageLayout.tsxLayout designed for the home page.
InnerpageLayoutInnerpageLayout.tsxStandard inner page layout — default fallback for content pages.
ArticleLayoutArticleLayout.tsxLayout optimised for article/editorial content.
GroupPageLayoutGroupPageLayout.tsxLayout for grouped or category-level pages.
ServerPageNavigationLayoutServerPageNavigationLayout.tsxLayout with server-rendered side navigation.
EmptyBlockLayoutEmptyBlockLayout.tsxFallback layout used when no matching layout is found.
    • BaseLayout.tsx
    • HomepageLayout.tsx
    • InnerpageLayout.tsx
    • ArticleLayout.tsx
    • GroupPageLayout.tsx
    • ServerPageNavigationLayout.tsx
    • EmptyBlockLayout.tsx

Placeholders

Each layout defines placeholder zones where widgets are rendered. Placeholder names are defined per layout — common names you will see across the platform layouts include Header, Left, Right, and Footer.

lib/constants.ts defines DROPPABLE_PLACEHOLDERS — the subset of placeholder names that support drag-and-drop reordering in Visor Studio’s Dev Mode:

export const DROPPABLE_PLACEHOLDERS = ["main", "left", "right"];

components/Placeholder.tsx receives the widget list for a given placeholder and delegates rendering of each widget to components/Widget.tsx.


Adding a New Layout

Create the layout component file

Create a new .tsx file in components/layouts/platform-layouts/. The filename without .tsx becomes the layout identifier used in Visor Studio — it must match exactly.

// components/layouts/platform-layouts/MyCustomLayout.tsx import Placeholder from "@/components/Placeholder"; import { LayoutProps } from "@/types/visor"; const MyCustomLayout = ({ placeholders, content, searchParams, draftMode, layout, }: LayoutProps) => { // In draft mode, delegate to the Client variant for live editing if (draftMode) { return ( <div className={`${layout?.layout}`}> {/* Render your ClientMyCustomLayout here */} </div> ); } // Production: iterate all placeholders defined in the layout config return ( <div className={`${layout?.layout}`}> {Object.entries(placeholders).map(([placeholderName, placeholder]) => ( <Placeholder key={placeholderName} placeholderName={placeholderName} widgets={placeholder.widgets} content={content} searchParams={searchParams} draftMode={draftMode} /> ))} </div> ); }; export default MyCustomLayout;

LayoutProps is imported from @/types/visor. It provides placeholders, content, searchParams, draftMode, slugSegments, and layout.

Export the layout JSON configuration

Each layout file should also export a layoutJsonConfiguration constant. Visor Studio reads this to auto-populate default widget slots when the layout is first registered.

export const layoutJsonConfiguration = { name: "My Custom Layout", layout: "MyCustomLayout", widgets: [ { sort: 0, id: "header", collection: "header", category: "Navigation", item: { id: "header", name: "Header", list: false, icon: "header-icon", placeholder_definition: { name: "Header" }, widget_definition: { name: "header" }, parameters: {}, }, }, // Add more default widget entries here... ], };

Each entry in widgets maps a widget to a placeholder_definition.name (e.g. "Header", "Left", "Right") — this tells Visor which placeholder zone the widget belongs to.

Create the Client variant for Dev Mode

For live editing support in Visor Studio’s Dev Mode, create a matching client component under platform-layouts/client/:

// components/layouts/platform-layouts/client/ClientMyCustomLayout.tsx "use client"; import { LayoutProps } from "@/types/visor"; export default function ClientMyCustomLayout({ placeholders, content, searchParams, draftMode, layout, }: LayoutProps) { // Client-side droppable implementation for Visor drag-and-drop editing return ( <div className={`${layout?.layout}`}> {/* Implement droppable placeholder zones here */} </div> ); }

Register the layout in Visor Studio

Go to Configurations → Layouts in Visor Studio and register the new layout. The layout name entered must match the component filename exactly (without .tsx).

Layout names are case-sensitive. Avoid duplicate filenames across layout folders — if a name appears in both platform-layouts/ and site-layouts/, behaviour is undefined.

Last updated on