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:
page.base.tsxfetches the page data and resolves the layout configuration from Visor.- The layout name from the configuration is passed to
components/Layout.tsx. Layout.tsxusesgenerateFileCollection()to scan all layout components and matches the layout name to a component filename (without extension).- The matched layout component is rendered, receiving the placeholder data.
- If no match is found,
EmptyBlockLayoutis 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:
| Layout | File | Description |
|---|---|---|
BaseLayout | BaseLayout.tsx | Minimal container wrapper used as a base for other layouts. |
HomepageLayout | HomepageLayout.tsx | Layout designed for the home page. |
InnerpageLayout | InnerpageLayout.tsx | Standard inner page layout — default fallback for content pages. |
ArticleLayout | ArticleLayout.tsx | Layout optimised for article/editorial content. |
GroupPageLayout | GroupPageLayout.tsx | Layout for grouped or category-level pages. |
ServerPageNavigationLayout | ServerPageNavigationLayout.tsx | Layout with server-rendered side navigation. |
EmptyBlockLayout | EmptyBlockLayout.tsx | Fallback 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.