standards
BlocksInterfaces

Blocks

Declare a block with defineBlock, type its inputs, and say where it may be placed.


A block is a typed building unit the workspace can place on a page, on a record or inside a display. You declare it in code with defineBlock: a name, how it looks in the picker, where it may go, and the inputs a person fills in. The inputs are validated for you, so a block never receives parameters it did not declare.

import { defineBlock, input } from "@standards/schema";

export const OPEN_DEALS = defineBlock({
  name: "open_deals",
  label: "Open deals",
  icon: "chart-bar",
  family: "data",
  contexts: ["page", "record"],
  inputs: {
    deals: input.query("deals"),
    measure: input.attribute("deals", { types: ["number", "currency"] }),
    period: input.period().optional(),
  },
});
FieldNotes
nameUnique API name of the block.
label, iconWhat the picker shows.
familydata, context, people or layout: the group the picker files it under.
contextsWhere the block may be placed: page, record, display.
inputsA record of named inputs, built with input.*.
layoutOptional: how the block sits on a page grid (see Layout).

contexts replaces the former requiresRecord flag. A block that needs the record it sits on is contexts: ["record"]; one that works anywhere lists all three.

Layout

layout tells a page how to size the block. Every key is optional.

export const OPEN_DEALS_BOARD = defineBlock({
  name: "open_deals_board",
  label: "Open deals board",
  icon: "chart-bar",
  family: "data",
  contexts: ["page"],
  layout: { size: { w: 8, h: 6 }, minWidth: 6, height: "flexible", minHeight: 4, maxHeight: 12 },
  inputs: { deals: input.query("deals") },
});
KeyDefaultNotes
size{ w: 4, h: 4 }The size when the block is added: w in columns (3, 4, 6, 8, 9 or 12), h in rows.
minWidth3The narrowest width the block accepts, one of the same steps.
height"flexible"fit (its content's height, capped by maxHeight, then scrolls), flexible (stretches between its bounds) or fill (the whole page).
minHeight2The lowest height of a flexible block, in rows.
maxHeightnoneThe highest height of a flexible block, or the cap of a fit one.
figurefalseA KPI figure: a group of figures renders as a band.

A page's .row() gives a block without .span(n) at least its minWidth, and a content's .height(mode) overrides height. See Pages.

Input kinds

InputHolds
input.object()The API name of an object
input.attribute(object, { types? })An attribute of that object, optionally limited to some attribute types
input.query(object?)A typed query(...) over an object
input.record(object?)The id of a record
input.agent(), input.routine(), input.view()The name of an agent, a routine or a view
input.url()An http or https link
input.enum(values)One of a closed list of strings
input.text(), input.number(), input.boolean()A scalar
input.period()day, week, month, quarter or year

Every input is immutable and required by default. .optional() makes it optional. .many() makes an attribute or enum input hold a non-empty list. An object-scoped input takes an object builder, which keeps attribute names checked, or an object name.

Two modifiers shape the block's settings panel without changing what it stores. .inSection("data" | "display") places the row with what the block reads or with how it shows it, when its kind would place it elsewhere (a filter switch reads, a trend attribute shows). .showWhen(key, { not }) shows the row beside the input it depends on, only once that input is set to none of the not values. A period that depends on an attribute shows only when the attribute is a date.

inputs: {
  measure: input.enum(["count", "sum"]).inSection("data"),
  attribute: input.attribute(QUERY_OBJECT).optional().showWhen("measure", { not: ["count"] }),
  by: input.attribute(QUERY_OBJECT).optional(),
  byBucket: input.period().optional().inSection("data").showWhen("by"),
},

A block that reads records measures them with the same query a collection uses, so its numbers follow the reader's permissions and visibility. See Aggregate.

Native block: Bubble

record_links (label Bubble, icon share, family context) draws the links around a record as a map of connected records and members. It may be placed on a record or on a page, and the default record view of an object created from now on includes it as a Bubble tab. On an existing view, add it from the "+" tab menu. It reads the record graph, so a reader only sees what they may see. On the map only the centre is named; another node shows its name while it is hovered, focused or selected.

InputHolds
centerrecord (default) or me: centre on a record, or on the viewer.
object, recordThe chosen record's object and id. On a record the block centres on the host record, so both stay empty. record requires object.
depth"1" or "2" (default): how many rings are expanded on open.

On a page there is no host record: set { object, record } or { center: "me" }, otherwise the block is refused.

Next steps