---
title: Fields
description: Describe the data type, the format, and the rendering of each piece of data in your app.
---

A field describes one piece of data: its name, its data type, its format, and how
the client renders it. Fields are the base of most Backlit screens. A table uses
fields for its columns, a datalist uses them for its labeled values, and a form uses
them for its inputs.

This introduction explains what a field is, where you declare it, and how to select
a field type. The page for each field type has the full options.

## What a field is

The field name is the key that Backlit reads from your records. The field type says
what the value is, such as text, a number, or a date. The options say what the value
means and how to show it.

```ts
fields.number('total', { label: 'Total', format: 'currency', currency: 'USD' })
```

This declaration says that each record has a `total` key, that the value is a
number, and that the number is an amount in US dollars. It does not say how to draw
a currency amount. The client decides that, with the locale of the user. A field
declares the meaning of a value, and the client owns the presentation.

## A field is not a database column

A field does not know where its data comes from. Many fields have the name of a
database column, because a view often returns database rows without changes. But
Backlit reads the value from the record that your view supplies, and your view can
build that record from any source.

Thus a field can describe a computed value, an aggregate, or a statistic. The
`openRate` field below has no column. The query calculates it.

```ts title="src/resources/newsletters.ts"
import { defineResource, fields, table } from '@backlit/sdk'

import { db } from '../db.ts'

const newsletters = defineResource('newsletters', [
  fields.text('subject', { label: 'Subject' }),
  fields.number('recipients', { label: 'Recipients', format: 'integer' }),
  fields.number('openRate', { label: 'Open rate', format: 'percent' }),
])

newsletters.view('list', async () => {
  // Each row has `subject`, `recipients`, and `openRate`. The query calculates the
  // last two from the deliveries table.
  const rows = await db.newsletters.listWithDeliveryStats()

  return table(rows).setFields(newsletters.pick('subject', 'recipients', 'openRate'))
})

export default newsletters
```

## Declare fields on a resource

Declare the fields of an entity one time, on its resource. Then select them where
you need them. `resource.pick` returns several fields in the order that you give,
and `resource.get` returns one field. The example below declares three fields and
uses them as the columns of a table. The protocol output shows what the client
receives.

<BacklitExample path="resources/fields" view="list" />

Because each screen uses the same declarations, a label or a format is written one
time. When you change `format` on the resource, each table, datalist, and form that
uses the field changes with it.

A screen can also adjust a field for its own use. `configure` and `clone` return a
new builder and do not change the declaration on the resource. You can also create a
field directly in a view, without a resource, for a value that only one screen
shows.

## Display and input

Each field has a read-only rendering and an input rendering. A `date` field is
formatted text in a table and a date picker in a form. Backlit calls these two uses
the `display` role and the `input` role, and the block selects the role. A table
uses `display`, and a form uses `input`.

Two options refine the rendering inside a role. `displayVariant` selects the
read-only presentation, such as `badge` for an enum. `inputVariant` selects the
input control, such as `textarea` for text.

For each role, the SDK converts the value of your record to a protocol value before
it sends the response. When a value cannot be converted, the SDK reports a defect and
omits that value. The other values of the block stay valid. To add a role of your
own, see [Field roles](/advanced/field-roles).

## Select a field type

| Your data                                                        | Field type                          |
| ---------------------------------------------------------------- | ----------------------------------- |
| A string, an email address, a URL, a phone number, or Markdown   | [Text](/reference/fields/text)       |
| An integer, a decimal, a currency amount, a percentage, progress | [Number](/reference/fields/number)   |
| A true or false value                                            | [Boolean](/reference/fields/boolean) |
| A calendar date, a date and time, a time, or a relative date     | [Date](/reference/fields/date)       |
| One or more values from a fixed set of options, such as a status | [Enum](/reference/fields/enum)       |
| Several record keys that read as one value, such as an address   | [Group](/reference/fields/group)     |
| A record that the current record points to, such as the customer of an order | [Ref](/reference/fields/ref) |
| Records that point back to the current record, such as the posts of an author | [Has](/reference/fields/has) |

When no built-in type fits your data, an extension can add a field type. See
[Extensions](/extensions).

## Shared field options

Field options accept `label`, `description`, `reveal`, and `enable` where
supported by the field contract. Use `revealWhen` and `enableWhen` to create
configured copies from typed form conditions. See [Form conditions](/reference/forms/conditions).
Simple fields also accept `link: linkTo(view, args)` for display navigation.
Relation displays put that link on the target field.

`configure` and `clone` return new builders. They do not change the resource's
stored declaration. Most block setters change their builder and return it.
Create data blocks and forms inside the resolver for each request.
