Skip to main content

Inputs

Input components return the current value so you can use it immediately in the same run:

const count = br.numberInput({ label: 'Count', defaultValue: 5 });
br.write({ body: `You picked ${count}.` });

When the user changes the value, Backroad re-runs your script with the new value bound to the same call site.

Buttons

Returns true exactly on the run after the click.

if (br.button({ label: 'Compute' })) {
// runs once after the user clicks
}

Download button

Triggers a file download in the browser when clicked. data is a function that produces the file contents; it's called only when the user clicks — never on the runs where the button is just sitting there — so an expensive payload costs nothing until it's actually wanted. The result also stays on the server and is streamed on demand, so it never rides along in the component tree on every rerun. Like button, it returns true exactly on the run after the click.

const downloaded = br.downloadButton({
label: 'Download report',
data: () => JSON.stringify({ status: 'ok' }, null, 2),
filename: 'backroad-report.json',
});

if (downloaded) {
// runs once after the user clicks
br.write({ body: 'Report downloaded!' });
}

filename defaults to "download", and mime is inferred from the filename extension (falling back to application/octet-stream) — so for common types you can omit it.

data may be async, and may return raw bytes (a Uint8Array or Buffer) for binary formats like images, PDFs, or zips:

br.downloadButton({
label: 'Export PDF',
filename: 'report.pdf', // mime inferred as application/pdf
data: async () => await renderPdf(), // returns a Buffer
});

Text input

const name = br.textInput({
label: 'Name',
placeholder: 'Type here',
defaultValue: '',
});

Number input

const age = br.numberInput({
label: 'Age',
min: 0,
max: 120,
step: 1,
precision: 0,
defaultValue: 18,
});

Text area

Multi-line text. Like textInput, the value commits when the field loses focus (so Enter inserts a newline instead of submitting).

const bio = br.textArea({
label: 'Bio',
placeholder: 'Tell us about yourself',
rows: 6,
defaultValue: '',
});

Slider

Drag (or use the arrow keys) to pick a number in a range. The value commits on release, not on every tick, so the script reruns once per change rather than once per pixel.

const volume = br.slider({
label: 'Volume',
min: 0,
max: 100,
step: 1,
defaultValue: 30,
});

Date & time

Native date and time pickers. dateInput returns an ISO YYYY-MM-DD string and timeInput returns a 24-hour HH:mm string; both return '' when nothing is selected.

const start = br.dateInput({
label: 'Start date',
min: '2026-01-01',
max: '2026-12-31',
defaultValue: '2026-06-15',
});

const remindAt = br.timeInput({
label: 'Reminder at',
defaultValue: '09:00',
});

Checkbox / toggle

Two flavors of the same boolean: br.checkbox renders a square check, br.toggle renders a switch.

const agreed = br.checkbox({ label: 'I agree', defaultValue: false });
const dark = br.toggle({ label: 'Dark mode' });

Radio

const size = br.radio({
label: 'Shirt size',
options: ['S', 'M', 'L', 'XL'],
defaultValue: 'M',
});

Select / multiselect

Powered by react-select under the hood — options is an array of { value, label }:

const fruit = br.select({
label: 'Fruit',
options: [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'cherry', label: 'Cherry' },
],
});

const toppings = br.multiselect({
label: 'Toppings',
options: [
{ value: 'cheese', label: 'Cheese' },
{ value: 'mushrooms', label: 'Mushrooms' },
{ value: 'pepperoni', label: 'Pepperoni' },
],
});
// toppings is an array of selected values

Color picker

const color = br.colorPicker({ label: 'Brand color', defaultValue: '#ff7a59' });

File upload

Returns an array of formidable.File objects — already saved to a temp location on the server, ready to read.

const files = br.fileUpload({
label: 'Upload CSVs',
accept: { 'text/csv': ['.csv'] },
multiple: true,
});

for (const f of files) {
// f.filepath, f.originalFilename, f.mimetype, f.size …
}

Try it