View
Register and build one addressable Backlit screen.
A view is the root of one screen. Register it on a resource with a unique name. Its resolver runs for each request and returns one content block, several content blocks, a redirect, or an expected error.
The SDK wraps normal content in a successful protocol response. The resolver does not create a success response.
API
resource.view(name, resolver)
resource.view(name, params, resolver)
resource.view(name, atoms, resolver)
resource.view(name, params, atoms, resolver)
The resolver receives the request context and a ViewBuilder.
| Method | Input | Result |
|---|---|---|
setTitle |
string |
Sets the screen title. |
setDescription |
string |
Sets supporting text under the title. |
The builder also provides view.state.filters when the view registers a filter bar.
Register a view
Calling resource.view registers the view and returns its definition. A view
name must be unique in its resource.
import { callout, defineResource } from '@backlit/sdk'
const orders = defineResource('orders', [])
orders.view('list', () => callout('Order list content'))
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "list",
"slots": {
"content": [
{
"kind": "callout",
"text": "Order list content"
}
]
}
}
}Basic content
Return one block from the resolver. The SDK puts it in the view content slot.
import { callout, defineResource } from '@backlit/sdk'
const orders = defineResource('orders', [])
orders.view(
'list',
() => callout('Order list content')
)
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "list",
"slots": {
"content": [
{
"kind": "callout",
"text": "Order list content"
}
]
}
}
}Multiple content blocks
Return an array to place several blocks in order.
import {
callout,
defineResource,
section,
} from '@backlit/sdk'
const orders = defineResource('orders', [])
orders.view(
'list',
() => [
section('Open orders'),
callout('12 orders need attention.'),
]
)
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "list",
"slots": {
"content": [
{
"kind": "section",
"title": "Open orders",
"slots": {
"content": []
}
},
{
"kind": "callout",
"text": "12 orders need attention."
}
]
}
}
}Title
Use the view builder to set the screen title.
import { callout, defineResource } from '@backlit/sdk'
const orders = defineResource('orders', [])
orders.view('list', (_ctx, view) => {
view.setTitle('Orders')
return callout('Order list content')
})
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "list",
"title": "Orders",
"slots": {
"content": [
{
"kind": "callout",
"text": "Order list content"
}
]
}
}
}Description
Add a description when the title does not give sufficient context.
import { callout, defineResource } from '@backlit/sdk'
const orders = defineResource('orders', [])
orders.view('list', (_ctx, view) => {
view.setDescription('Review and manage customer orders.')
return callout('Order list content')
})
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "list",
"description": "Review and manage customer orders.",
"slots": {
"content": [
{
"kind": "callout",
"text": "Order list content"
}
]
}
}
}Async resolver
The resolver can be async. Backlit waits for it before it builds the response.
import { callout, defineResource } from '@backlit/sdk'
const orders = defineResource('orders', [])
orders.view('list', async () => {
const count = await Promise.resolve(12)
return callout(`${count} orders need attention.`)
})
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "list",
"slots": {
"content": [
{
"kind": "callout",
"text": "12 orders need attention."
}
]
}
}
}Parameters
Declare parameters in their address order. Read their typed names from
ctx.paramValues. The protocol node contains the argument values, not the
parameter names.
import { callout, defineResource } from '@backlit/sdk'
const orders = defineResource('orders', [])
orders.view('detail', ['id'], (ctx) =>
callout(`Order ${ctx.paramValues.id}`)
)
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "detail",
"args": [
"42"
],
"slots": {
"content": [
{
"kind": "callout",
"text": "Order 42"
}
]
}
}
}Complete address
Call definition.getAddress with arguments in declaration order. It returns the
resource, view, string arguments, and a complete string key. A missing argument
causes an error.
import { callout, defineResource } from '@backlit/sdk'
const orders = defineResource('orders', [])
const detail = orders.view('detail', ['id'], (ctx) =>
callout(`Order ${ctx.paramValues.id}`)
)
const detailAddress = detail.getAddress([42])
orders.view('address', () =>
callout(`Address: ${detailAddress.key}`)
)
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "address",
"slots": {
"content": [
{
"kind": "callout",
"text": "Address: orders/detail/42"
}
]
}
}
}Redirect
Return ctx.redirectTo when another view must handle the request. Pass the target
view definition. A target with parameters also requires its arguments in
declaration order. Numbers are converted to strings.
import { callout, defineResource } from '@backlit/sdk'
const orders = defineResource('orders', [])
const orderList = orders.view('list', () =>
callout('Order list content')
)
orders.view(
'legacy',
(ctx) => ctx.redirectTo(orderList)
)
export default orders{
"kind": "redirect",
"status": 302,
"target": {
"kind": "view",
"resource": "orders",
"view": "list"
}
}Expected error
Return ctx.error for an expected request failure. Pass an HTTP error status
and a stable error code.
import { defineResource } from '@backlit/sdk'
const orders = defineResource('orders', [])
orders.view(
'private',
(ctx) => ctx.error(403, 'E_FORBIDDEN')
)
export default orders{
"kind": "error",
"status": 403,
"code": "E_FORBIDDEN"
}Filters
Register a filter bar in the state object. Place view.state.filters in a
panel or section. Read its values with ctx.parse(view).filters.
import {
defineFilters,
defineResource,
filters,
section,
} from '@backlit/sdk'
const orders = defineResource('orders', [])
const orderFilters = defineFilters([
filters.text('search').setLabel('Search orders'),
])
orders.view(
'list',
{ filters: orderFilters },
(_ctx, view) =>
section('Orders').setFilters(view.state.filters)
)
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "list",
"slots": {
"content": [
{
"kind": "section",
"title": "Orders",
"slots": {
"content": [],
"filters": {
"kind": "filters",
"resource": "orders",
"view": "list",
"keyword": "f",
"definesResult": true,
"dependsOnResult": false,
"filters": [
{
"kind": "text",
"name": "search",
"label": "Search orders"
}
]
}
}
}
]
}
}
}See Filters for all filter types and binding rules.
Zones
Return a zone in the view content when that part of the screen must refresh on its own.
import {
defineResource,
fields,
widget,
zone,
} from '@backlit/sdk'
const orders = defineResource('orders', [])
const revenue = fields.number('revenue', {
format: 'currency',
currency: 'USD',
})
orders.view(
'overview',
() =>
zone('revenue', () =>
widget()
.setTitle('Revenue')
.setStat({ value: 48_120, field: revenue })
)
)
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "overview",
"slots": {
"content": [
{
"kind": "zone",
"name": "revenue",
"resource": "orders",
"view": "overview",
"slots": {
"content": [
{
"kind": "widget",
"title": "Revenue",
"stat": {
"value": 48120,
"field": {
"kind": "number",
"name": "revenue",
"format": "currency",
"currency": "USD",
"label": ""
},
"delta": "absolute"
}
}
]
}
}
]
}
}
}See Zone for zone rules and combinations.
All options
This example uses parameters, filters, a title, a description, a section, and a zone in one view.
import {
defineFilters,
defineResource,
fields,
filters,
section,
widget,
zone,
} from '@backlit/sdk'
const orders = defineResource('orders', [])
const orderFilters = defineFilters([
filters.text('search').setLabel('Search orders'),
])
const total = fields.number('total', {
format: 'currency',
currency: 'USD',
})
orders.view(
'detail',
['id'],
{ filters: orderFilters },
(ctx, view) => {
view
.setTitle(`Order ${ctx.paramValues.id}`)
.setDescription('Order status and totals.')
return section('Summary')
.setFilters(view.state.filters)
.setContent([
zone('total', () =>
widget()
.setTitle('Total')
.setStat({ value: 128, field: total })
),
])
}
)
export default orders{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "detail",
"args": [
"42"
],
"title": "Order 42",
"description": "Order status and totals.",
"slots": {
"content": [
{
"kind": "section",
"title": "Summary",
"slots": {
"content": [
{
"kind": "zone",
"name": "total",
"resource": "orders",
"view": "detail",
"args": [
"42"
],
"slots": {
"content": [
{
"kind": "widget",
"title": "Total",
"stat": {
"value": 128,
"field": {
"kind": "number",
"name": "total",
"format": "currency",
"currency": "USD",
"label": ""
},
"delta": "absolute"
}
}
]
}
}
],
"filters": {
"kind": "filters",
"resource": "orders",
"view": "detail",
"args": [
"42"
],
"keyword": "f",
"definesResult": true,
"dependsOnResult": false,
"filters": [
{
"kind": "text",
"name": "search",
"label": "Search orders"
}
]
}
}
}
]
}
}
}Breadcrumb
view.setBreadcrumb(record, { ancestors, fields? }) sets the header path.
Ancestors are visit targets with fixed arguments and text or icons. Optional
fields display the current record. Without fields, the header uses the view
title. Breadcrumb fields always use the display role.
{
"kind": "success",
"status": 200,
"node": {
"kind": "view",
"resource": "orders",
"name": "detail",
"args": [
"42"
],
"title": "Order details",
"breadcrumb": {
"ancestors": [
{
"kind": "visit",
"resource": "orders",
"view": "list",
"text": "Orders"
}
],
"record": {
"fields": [
{
"kind": "text",
"name": "number",
"label": ""
}
],
"data": {
"number": "ORD-42"
}
}
},
"slots": {
"content": [
{
"kind": "callout",
"text": "Paid in full."
}
]
}
}
}The view definition also exposes resource, name, params, atoms,
getAddress, and agent options. Use Agent tools to opt in.
The app calls resolve or resolveZone after middleware and address checks.