Skip to main content
Layouts are registered in the @workspace/form-renderer package. Each layout is a self-contained directory with two files — a manifest and a shell component — plus a single line in the registry.

File structure


Step 1 — Create the manifest

manifest.ts declares the layout’s identity, its SVG thumbnail (shown in the Design panel picker), and the config fields it accepts.

LayoutManifest reference

LayoutConfigField reference


Step 2 — Create the shell component

layout.tsx is a React component that receives LayoutShellProps and renders the page structure. It is responsible for placing slots.formCard wherever the form card should appear.

LayoutShellProps reference

Always read config values defensively with fallbacks. If a respondent has an old theme saved without your new config keys, those keys will be missing.

Step 3 — Using the runner (advanced)

The runner prop is only available in the live landing app — not in the studio canvas. It gives you access to the current question node and the respondent’s traversal state. The Split layout uses this to show the current question title in its sidebar.
If runner is undefined (studio preview), the sidebar simply renders without question content. This is the expected behaviour — guard all runner access with optional chaining.

Step 4 — Register the layout

Add a single import and one entry to registry.ts:
That is everything. No other files need changing. The layout will immediately appear in:
  • The Design panel thumbnail picker (both apps)
  • The getAllLayouts() export (for any custom UI that lists layouts)
  • The resolveLayout("my-layout") lookup (used at render time)

Mobile considerations

The studio canvas renders your layout at full width. On mobile the layout is shown inside a scaled device frame. A few conventions to follow:
  • Use md: Tailwind breakpoints for desktop-only elements (as the Split layout’s sidebar does)
  • Provide a fallback mobile treatment — a slim fixed header is a good pattern
  • Avoid fixed widths in px on the outermost wrapper; use % or vw so device-frame scaling works correctly

Complete example

The built-in Split layout is a good reference for a more complex layout using the runner:

Next steps

Layouts overview

See the three built-in layouts and their config options.

Content-type plugins

Build custom question types with their own rendering logic.