Swarmz

Templates

Re-author the sidebar and dashboard structure by composing typed building blocks

Tokens and CSS restyle a fixed layout. The Templates tier changes the layout itself: you compose the sidebar and the dashboard from a closed palette of registered components, expressed as a typed JSON tree — structure as data, never code.

Templates are admin-enabled (templates_enabled on your account — contact Swarmz support). With the capability on, the Branding studio's Templates mode shows the builder; without it, the mode falls back to Guided.

The model

A template names registered component types and wires them to app data through declared bindings:

{
  "apiVersion": 1,
  "sidebar": {
    "apiVersion": 1,
    "root": {
      "type": "Stack",
      "props": { "gap": "md", "padding": "md" },
      "children": [
        { "type": "LogoSlot", "bindings": { "logo": "brand.logo" } },
        { "type": "WorkspaceButton", "bindings": { "workspace": "workspace" } },
        { "type": "NavList", "bindings": { "items": "nav.items" } },
        { "type": "Divider" },
        { "type": "CreditsBlock", "bindings": { "credits": "credits" } },
        { "type": "SignOutButton", "props": { "label": "Sign out" } }
      ]
    }
  }
}

Each surface (sidebar, dashboard) is optional — author one and the other keeps its stock layout. If a template fails validation, the surface falls back to the stock UI; your customers never see a broken shell.

The component palette

Layout primitives — valid on both surfaces:

TypePropsChildren
Stackgap, padding, alignyes
Rowgap, padding, alignyes
Gridcols (1/2/3), gapyes
Boxgap, padding, alignyes
Spacersizeno
Dividerno
Textcontent (≤200 chars), size, weightno

Spacing props take none / xs / sm / md / lg; align takes start / center / end / stretch. Text may bind its content to brand.name instead of a literal.

Sidebar leaves — valid in sidebar only:

TypeBindsActions
NavListnav.items (your resolved navigation)
WorkspaceButtonworkspaceopenWorkspaceSwitcher
CreditsBlockcredits
LogoSlotbrand.logo
SignOutButton— (label prop, ≤40 chars)signOut

Dashboard leaves — valid in dashboard only:

TypeBindsActions
Greetinggreeting.text
PromptBlockpromptcreateProject
ProjectGridprojects.items
TabStriptabs.items (your surface config tabs)selectTab
FolderRowfolders.itemsopenFolder
NewProjectTile— (label prop, ≤40 chars)createProject

Bindings and actions are closed vocabularies: a component can only bind the sources and wire the actions its registry entry declares. Using a sidebar leaf in a dashboard template (or vice versa) drops that node.

The slot model

Templates build on the app's semantic markup contract: every region a template can address carries a stable data-slot attribute, scoped to its owning data-part. Slot names are public, versioned identifiers — a slot's address is the (part, slot) pair, never a bare slot name.

The published inventory:

PartSlots
SidebarRootsidebar (the whole sidebar), logo, nav, workspace (nested inside nav), footer
DashboardRootdashboard, prompt, projects, tabs (nested), folders (nested)
PopoverContentworkspace-menu (the workspace-switcher popover instance), header, credits, actions, workspaces

The same hooks are targetable from raw CSS, so a template restructure and a stylesheet can share one addressing scheme.

Validation and caps

A template is treated as untrusted data and normalized before storing or rendering:

  • apiVersion must be 1 (top-level and per surface) — anything else is rejected and the stock UI renders.
  • Unknown component types, unknown props, out-of-allowlist bindings, and children on childless components are dropped with diagnostics.
  • React/DOM escape hatches (style, ref, key, dangerouslySetInnerHTML, any on* handler) are dropped unconditionally.
  • URL props must be https:// or an internal /path; string props are HTML-stripped and length-capped.
  • Caps: 200 nodes total, 12 levels deep. Exceeding either rejects the template.

The builder surfaces every diagnostic as you edit, so a dropped node is visible long before publish.

On this page