macbetter

Text and fields

Token Field

Enter a list of people, tags or addresses as tokens.

​Hollis Grant token​​Inès Moreau token​
import { Field, FieldLabel } from "macbetter/field"
import { TokenField } from "macbetter/token-field"

export default function Example() {
  return (
    <div style={{ width: "100%", maxWidth: 420 }}>
      <Field layout="horizontal">
        <FieldLabel>To:</FieldLabel>
        <TokenField
          name="recipients"
          placeholder="Add people"
          defaultValue={[
            { type: "token", value: "hollis@example.com", label: "Hollis Grant" },
            { type: "token", value: "ines@example.com", label: "Inès Moreau" },
          ]}
        />
      </Field>
    </div>
  )
}

When to use it

Use a token field when each entry is a thing of its own: recipients in a message, tags on a board, email domains on an allow list. Each entry becomes a token that moves, selects and deletes as one piece, and the text around it stays editable.

Type an entry and a comma, or press Return, and it becomes a token. Leaving the field does the same for what's left. Backspace after a token removes it.

Usage

import { TokenField } from "macbetter/token-field"

The value is an array of text and tokens. A token has a type, a value for your app and a label people read. Give the field a name and it submits the tokens as text.

Tags with remove buttons

removable draws a remove button on each token, for touch screens and for people who don't think of Backspace. createToken turns typed text into a token, or returns null to leave it as text, which is where you'd check an email address or look up a person.

​finance token​​weekly token​​board-ready token​
Type a tag and a comma or Return.
import { Field, FieldDescription, FieldLabel } from "macbetter/field"
import { TokenField } from "macbetter/token-field"

export default function Example() {
  return (
    <div style={{ width: "100%", maxWidth: 340 }}>
      <Field name="tags">
        <FieldLabel>Tags</FieldLabel>
        <TokenField
          removable
          placeholder="Add a tag"
          defaultValue={[
            { type: "token", value: "finance", label: "finance" },
            { type: "token", value: "weekly", label: "weekly" },
            { type: "token", value: "board-ready", label: "board-ready" },
          ]}
        />
        <FieldDescription>Type a tag and a comma or Return.</FieldDescription>
      </Field>
    </div>
  )
}

For suggestions as people type, put a Trigger Menu from @ui-sdk/react/trigger-menu inside the field as a child.

Platform behavior

  • macOS. The bordered field with 12-point tokens on a soft accent tint with a hairline edge and 4-point corners. A selected token fills with the accent, like a selected row.
  • iPadOS and iOS. The filled gray field with 28-point capsule tokens and 16-point text.
  • Every platform. Tokens wrap onto new lines as the field fills, and the field grows with them.

Accessibility

  • The editor is a text box; each token reads as its label and kind, and inserting or removing one is announced.
  • Arrow keys cross a token in one press. Backspace and Delete remove the token beside the caret; Command-Z brings it back.
  • Name the field with a Field label or aria-label. Remove buttons are hidden from assistive technology, since Backspace does the same thing.

API

TokenField

PropTypeDefaultDescription
size"sm" | "md" | "lg""md"Minimum height and type size.
variant"auto" | "bordered" | "filled""auto"Bordered on macOS and filled on touch, or a fixed look.
placeholderstring—Shown while the field is empty.
tokenizeOnstring[] | false[","]Characters that turn the text before them into a token. Return and leaving the field do too.
createToken(text) => Token | null—Makes a token from typed text, or leaves it as text.
removablebooleanfalseDraws a remove button on each token.
renderToken(token) => ReactNode—Your own token content, usually a TokenFieldToken.
defaultValueTokenInputValue—Text and tokens it starts with.
onValueChange(value, details) => void—Runs on typing, paste, undo or a token change.
controllerTokenInputController—Inserts, updates and removes tokens from code.
editorPropsobject—More props for the element that draws the field.

TokenFieldToken and TokenFieldTokenRemove take the props of a span, for use inside renderToken.

Data attributes

AttributeOnValues
data-size, data-variantFieldAs above
data-emptyFieldPresent while it holds nothing
data-selectedTokenPresent while the token is selected
data-pending, data-unresolvedTokenLoading, or needs the person's attention
data-token-typeTokenThe token's type

CSS variables

VariablemacOSiOSWhat it sets
--mb-token-field-token-height18px28pxToken height
--mb-token-field-token-radius4pxCapsuleToken corners
--mb-token-field-tintAccentAccentWhat tokens are tinted with