Skip to main content
Blocks can’t open dialogs or pages. When an action needs more input than a click, the block expands in place: a toggle button opens a section, and the section holds a form. Opening a section, switching tabs and typing into a form happen in the viewer’s browser and never call your app.

Sections

Declare section names on the block, then use the handles from the render context:
Handles are typed. A string such as section="details" is a type error, and so is a handle for a section the block didn’t declare. A section without a handle is a plain titled group and can’t be toggled. Section names are 1 to 48 characters from A-Z, a-z, 0-9, _ and -, at most 32 per block. reintersect check reports a handle used in two sections and a handle from another block. Each viewer opens and closes sections on their own; the open state isn’t stored or shared. In read-only views, collapsed sections stay collapsed and their toggle buttons are hidden.

Where sections go

Sections sit at the block, tab or form level, one deep, so a section can’t hold another section. A section can hold Columns, Tabs and a Form. It can’t hold an Actions row: the toggle button and any other buttons go in the block’s root Actions, which always comes last in the body. A collapsed section needs a title or a toggle button that targets its handle, so viewers have a way to open it.

Forms

A form collects input for an action. Give the action an input schema:
Then render a Form with that action. Its child is a function that receives one typed field handle per input property:
Field handles carry their value type, and each input component accepts only fields it can edit: Binding a boolean field to DateTimePicker, or misspelling fields.deadlne, is a type error. Passing an action without an input schema to Form is also a type error. Field names are unique across the whole block, so two forms can’t both have a reason field. Fields inside a form can’t carry audience; put audience on the Form itself.
A plain string field also binds to DateTimePicker, because the type system can’t see string formats. reintersect check reads the JSON Schema format and reports a field component that doesn’t fit the field, or a form that leaves out a required field.

Submitting

submitLabel is required. The form ends with its own row holding only the submit button, labelled with submitLabel, so there’s no cancel button and no other buttons in it. When the block has no root Actions and this is its only form, the submit button is the block’s primary button; otherwise it’s secondary. A form sits at the block, section or tab level, and forms don’t nest. Inside a form you can use sections and columns to arrange the fields. The browser enforces required and maxLength before sending. The SDK then validates the values against the action’s input schema before your handler runs; values that don’t match fail the action with an error. For checks the schema can’t express, return fieldErrors: the form keeps the member’s values and shows each message beside its field. On success, the form clears. Unsent values live in the viewer’s browser, keyed by field name, so they survive the block re-rendering while the member types.

Args and input together

An action can take both args, bound when the view renders, and input, typed by the member:

Tabs

Tabs split a block into two to six views that each viewer switches between locally. Tabs sit at the block or section level and can’t be nested, and a tab can hold sections, columns and a form. Tabs that don’t fit move into a More menu. In read-only views and agent text, every tab is shown one after the other under its label. See Tabs.