Skip to content
Backlit
Esc
navigateopen⌘Jpreview
On this page

Blocks

Compose the content and the layout of a screen from blocks.

A block is a part of a screen. A view returns blocks, and the client renders each block with its own component. You compose a screen by putting blocks inside other blocks, in the same way that you compose a React tree from components.

This introduction explains the two categories of blocks, how to compose a layout from them, and how to select a block. The page for each block has the full options.

Two categories of blocks

A data block displays the data that your view supplies. table, list, datalist, and timeline are data blocks. You give a data block your records and the fields that it must show. The SDK converts each value to its protocol form, and the client renders it.

table(rows).setFields(orders.pick('reference', 'customer', 'total'))

A container block gives structure to a page. section, panel, tabs, and group are container blocks. A container block has a content slot that holds other blocks, and some container blocks have a second slot. A section has an aside slot for content that goes next to the main content.

section('Orders').setContent([panel([table(rows).setFields(columns)])])

Forms and widgets are blocks too, and they go in the same slots. They have their own introductions: Forms and Widgets.

Compose a layout

Backlit has no grid, no columns, and no spacing options. You declare the structure of a page, and the client decides the layout from that structure and from the width that is available. A section with an aside renders as two columns on a wide screen and as one column on a narrow screen, and your view does not change.

The example below builds a detail page. The section gives the page a heading. Its content slot has a panel, and its aside slot has a second panel.

import {
  callout,
  defineResource,
  panel,
  section,
} from '@backlit/sdk'

const orders = defineResource('orders', [])

orders.view('detail', () =>
  section('Order')
    .setContent([
      panel([
        callout('The order is ready to ship.'),
      ]).setTitle('Activity'),
    ])
    .setAside([
      panel([callout('A. Customer')]).setTitle('Customer'),
    ])
)

export default orders
{
  "kind": "success",
  "status": 200,
  "node": {
    "kind": "view",
    "resource": "orders",
    "name": "detail",
    "slots": {
      "content": [
        {
          "kind": "section",
          "title": "Order",
          "slots": {
            "content": [
              {
                "kind": "panel",
                "title": "Activity",
                "slots": {
                  "content": [
                    {
                      "kind": "callout",
                      "text": "The order is ready to ship."
                    }
                  ]
                }
              }
            ],
            "aside": [
              {
                "kind": "panel",
                "title": "Customer",
                "slots": {
                  "content": [
                    {
                      "kind": "callout",
                      "text": "A. Customer"
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  }
}

The content of a slot is an array of builders, so you compose it with normal TypeScript. Use a condition or an array spread to include a block for some requests only. Create the blocks inside the view callback, so each request gets new builders.

section('Order').setContent([
  ...(order.isOverdue ? [callout('The payment is overdue.').setTone('warning')] : []),
  panel([datalist(order).setFields(orders.pick('reference', 'customer', 'total'))]),
])

Select a block

Your need Block
Show many records in columns, to scan and compare them Table
Show many records as rows with a title, a description, and metadata List
Show events in time order Timeline
Show one record as labeled values, for a detail page Datalist
Show a short message next to the content that it describes Callout
Divide a page into regions with a heading, filters, and actions Section
Put content inside one surface with a boundary Panel
Switch between sets of content on one page Tabs
Keep related blocks together without a heading or a boundary Group
Show one select filter as a row of buttons above a list Segments

Two pairs of blocks are easy to confuse.

Use a section for a region of the page and a panel for one item with a boundary. A list page is usually a section that has the filters and the actions, with a panel inside it that has the table.

Use tabs to switch between different content, and segments to switch one list between the values of one field, such as the status of a subscriber.

Block metadata

Most blocks accept the same optional metadata: a title, a description, a badge, and a footnote. You set them with methods on the builder, such as setTitle. The client controls their position and their style, so a title looks the same on each block of your app.

Blocks and zones

A block gets its data from the view callback, so a change to the data needs a new request for the view. When a part of a page must load again without the other parts, put it in a zone. A zone goes in the same slots as a block, and it has its own callback. See Zones.

When no built-in block fits, an extension can add a block. See Extensions.

Was this page helpful?