Skip to main content

Containers

Containers are nodes in the Backroad tree that can hold other nodes — components or further containers. Every Backroad app is a tree of containers with leaf components hanging off them.

When your script first runs, br is bound to the implicit root container for the current page. Calling a container method (br.columns, br.tabs, br.collapse, br.sidebar, br.chatMessage) returns a new br-like manager scoped to that child container. Any br.write/br.button/etc. calls on the returned manager add children inside that container.

The container methods

MethodWhat it makes
br.page({ path })A separate route. Calling br.page always starts a new top-level page tree.
br.sidebar({})A persistent left rail rendered on every page.
br.columns({ columns })A row split into N equal columns, or into ratios given as [1, 2, 1]. Returns one manager per column.
br.tabs({ labels })A tab strip. Returns one manager per tab body.
br.collapse({ label })A click-to-expand panel.
br.chatMessage({ by, avatar?, avatarPlacement?, loadingPromise? })A single chat bubble — used by the LLM components.
br.base({})A bare grouping container. Rarely used directly.

Columns

br.columns returns an array of managers — one per column. Iterate it, or destructure when you know the count:

import { run } from '@backroad/backroad';

run((br) => {
const [left, right] = br.columns({ columns: 2 });
left.write({ body: '## Left' });
left.button({ label: 'Left button' });

right.write({ body: '## Right' });
right.button({ label: 'Right button' });
});

Pass an array of numbers to control the ratio:

const [narrow, wide] = br.columns({ columns: [1, 3] }); // 25% / 75%

Tabs

const [overview, details] = br.tabs({ labels: ['Overview', 'Details'] });
overview.write({ body: 'Top-level summary lives here.' });
details.write({ body: 'Drill-down content lives here.' });

The returned array's length always matches labels.length.

Collapse

const advanced = br.collapse({ label: 'Advanced options' });
advanced.numberInput({ label: 'Retry count', defaultValue: 3 });
advanced.toggle({ label: 'Verbose logging' });
const sidebar = br.sidebar({});
sidebar.write({ body: '## Filters' });
const region = sidebar.select({
label: 'Region',
options: [
{ label: 'EU', value: 'eu' },
{ label: 'US', value: 'us' },
],
});

Pages

br.page({ path }) always anchors to the root, regardless of which manager you call it on. It defines a new route that the navbar will expose:

run((br) => {
const home = br.page({ path: '/' });
home.write({ body: '# Home' });

const settings = br.page({ path: '/settings' });
settings.toggle({ label: 'Dark mode' });
});

Try it