# Components (/docs/products/dash-dots/developers/components)

Components look and behave like the rest of Dash. Each name below links to its page in the [Components reference](/docs/products/dash-dots/reference/components), which lists every prop and event.

Every prop is optional. If you use a prop that a component doesn't have, the build tells you. Events work as described in [Views](/docs/products/dash-dots/developers/views#events).

## Lay out a page

| Component                                                                                                                           | Use it for                                                                                                                  |
| ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| [`View`](/docs/products/dash-dots/reference/components/view)                                                                        | Columns, rows and cards. See [Layout and styling](/docs/products/dash-dots/developers/views#layout-and-styling)             |
| [`Stack`](/docs/products/dash-dots/reference/components/stack)                                                                      | Text over a picture, and other layers. See [Layers with Stack](/docs/products/dash-dots/developers/views#layers-with-stack) |
| [`Grid`](/docs/products/dash-dots/reference/components/grid), [`GridItem`](/docs/products/dash-dots/reference/components/grid-item) | A home screen of services, in equal columns                                                                                 |
| [`Scroll`](/docs/products/dash-dots/reference/components/scroll)                                                                    | A row that scrolls sideways                                                                                                 |
| [`Carousel`](/docs/products/dash-dots/reference/components/carousel)                                                                | Slides, one at a time                                                                                                       |
| [`Footer`](/docs/products/dash-dots/reference/components/footer)                                                                    | The page's main button, kept at the bottom while the page scrolls. Put it last                                              |
| [`Pinned`](/docs/products/dash-dots/reference/components/pinned)                                                                    | Anything else kept on the top or bottom edge, such as [the page's own tabs](#a-pages-own-navigation)                        |
| [`Divider`](/docs/products/dash-dots/reference/components/divider)                                                                  | A line between sections                                                                                                     |

## Show text, amounts and pictures

| Component                                                         | Use it for                                                                           |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| [`Text`](/docs/products/dash-dots/reference/components/text)      | All text. `variant` sets its role, from `display` for a big amount down to `caption` |
| [`Link`](/docs/products/dash-dots/reference/components/link)      | Text to tap. An https `href` opens in the phone's browser                            |
| [`Image`](/docs/products/dash-dots/reference/components/image)    | A picture from `statics/`, or from a host in `network.hosts`                         |
| [`Avatar`](/docs/products/dash-dots/reference/components/avatar)  | A photo in a circle, or initials until it loads                                      |
| [`Icon`](/docs/products/dash-dots/reference/components/icon)      | One of Dash's [icons](/docs/products/dash-dots/reference/components#icon-names)      |
| [`Badge`](/docs/products/dash-dots/reference/components/badge)    | A short status, like Paid                                                            |
| [`QRCode`](/docs/products/dash-dots/reference/components/qr-code) | A ticket or other code for someone else to scan                                      |

```xml
<View variant="card" gap="small">
  <View direction="row" gap="medium" align="center">
    <Avatar name="Mariama Kamara" image="{{ photo }}" />
    <Text variant="subtitle">Mariama Kamara</Text>
  </View>
  <Text variant="caption" tone="muted">Balance</Text>
  <Text variant="display" numeric>SLE {{ balance }}</Text>
</View>
```

Set `numeric` on amounts so the digits line up. Give an `Image` an `aspect`, and its height follows from its width.

## Lists and details

A [`List`](/docs/products/dash-dots/reference/components/list) holds [`ListItem`](/docs/products/dash-dots/reference/components/list-item) rows in a card. For a receipt, use a [`DetailRow`](/docs/products/dash-dots/reference/components/detail-row) for each label and its value. A [`Disclosure`](/docs/products/dash-dots/reference/components/disclosure) is a row that opens to show more, like an FAQ answer. [`Steps`](/docs/products/dash-dots/reference/components/steps) with [`Step`](/docs/products/dash-dots/reference/components/step) show what has happened and what comes next.

This list of payments loads more as the person scrolls down:

```xml
<List onEndReached="loadMore">
  <For each="{{ payments }}" item="payment" key="id">
    <ListItem
      icon="receipt"
      title="{{ payment.name }}"
      value="SLE {{ payment.amount }}"
      data-id="{{ payment.id }}"
      onTap="open"
    />
  </For>
</List>
```

`loadMore` returns early while a batch is loading, and after the last one:

```ts
async loadMore(state) {
  if (state.loading || state.allLoaded) {
    return;
  }
  this.setState({ loading: true, error: '' });
  try {
    const url = `${env.API_URL}/payments?skip=${state.payments.length}`;
    const response = await this.dash.network.http.request<Payment[]>({ url, as: 'json' });
    if (!response.ok) {
      throw new Error();
    }
    this.setState({
      payments: [...this.state.payments, ...response.body],
      allLoaded: response.body.length === 0,
    });
  } catch {
    this.setState({ error: "We couldn't load more payments." });
  } finally {
    this.setState({ loading: false });
  }
},
```

## Long text to read

For news and guides, wrap your text in [`Prose`](/docs/products/dash-dots/reference/components/prose) for comfortable spacing. It works with [`Quote`](/docs/products/dash-dots/reference/components/quote) and [`BulletList`](/docs/products/dash-dots/reference/components/bullet-list). For Markdown from your server, use [`RichText`](/docs/products/dash-dots/reference/components/rich-text), which is safe: HTML shows as plain text, and only https links open.

## Forms

| Component                                                                                                                                 | Use it for                                                                                                                                                                                                    |
| ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`Input`](/docs/products/dash-dots/reference/components/input)                                                                            | One line of text. `type` picks the keyboard                                                                                                                                                                   |
| [`TextArea`](/docs/products/dash-dots/reference/components/text-area)                                                                     | Several lines of text                                                                                                                                                                                         |
| [`Select`](/docs/products/dash-dots/reference/components/select), [`Option`](/docs/products/dash-dots/reference/components/option)        | One choice from a long list                                                                                                                                                                                   |
| [`RadioGroup`](/docs/products/dash-dots/reference/components/radio-group), [`Radio`](/docs/products/dash-dots/reference/components/radio) | One choice from a few                                                                                                                                                                                         |
| [`Switch`](/docs/products/dash-dots/reference/components/switch), [`Checkbox`](/docs/products/dash-dots/reference/components/checkbox)    | On or off                                                                                                                                                                                                     |
| [`Stepper`](/docs/products/dash-dots/reference/components/stepper), [`Slider`](/docs/products/dash-dots/reference/components/slider)      | A number                                                                                                                                                                                                      |
| [`Button`](/docs/products/dash-dots/reference/components/button)                                                                          | An action. `variant` is `primary`, `secondary`, `outline` or `ghost`, `tone` colors it, like `danger` for one that deletes, and `size` makes it smaller or larger. `loading` shows a spinner and ignores taps |

A field shows what's in state. Pass the value in, and update it from the field's `change` event. This one sends airtime to a cousin in Kenema:

```xml
<Input
  label="Phone number"
  type="phone"
  prefix="+232"
  value="{{ phone }}"
  error="{{ phoneError }}"
  onChange="setPhone"
/>
<Select label="Network" value="{{ network }}" onChange="setNetwork">
  <Option value="orange" label="Orange" />
  <Option value="africell" label="Africell" />
  <Option value="qcell" label="Qcell" />
</Select>
<Switch label="Save this number" checked="{{ save }}" onChange="setSave" />
<Button onClick="send" loading="{{ sending }}">Send airtime</Button>
```

```ts
mutations: {
  setPhone: (_state, event: ValueEvent) => ({ phone: event.detail.value, phoneError: '' }),
  setNetwork: (_state, event: ValueEvent) => ({ network: event.detail.value }),
  setSave: (_state, event: CheckedEvent) => ({ save: event.detail.checked }),
},
actions: {
  async send(state) {
    if (state.phone.replace(/\D/g, '').length !== 8) {
      this.setState({ phoneError: 'Enter 8 digits, such as 76 123456.' });
      return;
    }
    // Send the airtime.
  },
},
```

Set `error` to show a problem under a field, as `send` does. For a search box, use `type="search"` and see [Search as the person types](/docs/products/dash-dots/developers/dash-apis#search-as-the-person-types).

### Amounts and codes

[`AmountInput`](/docs/products/dash-dots/reference/components/amount-input) takes a sum of money. [`CodeInput`](/docs/products/dash-dots/reference/components/code-input) takes a one-time code or PIN, and picks up a code the phone reads from an SMS.

```xml
<AmountInput label="Amount" value="{{ amount }}" onChange="setAmount" />
<CodeInput
  label="Enter the code we sent you"
  value="{{ code }}"
  onChange="setCode"
  onComplete="verify"
/>
```

- `AmountInput` counts in the smallest unit, like the Monime API, so `150` is SLE 1.50. Set `unit="major"` to count whole amounts, so `150` is SLE 150. It sends `null` when empty, so type its event as `ValueEvent<number | null>`.
- `CodeInput` sends `complete` once the last digit is in, so check the code there.

## Tabs and filters

[`Tabs`](/docs/products/dash-dots/reference/components/tabs) switch between views of one page. [`Chip`](/docs/products/dash-dots/reference/components/chip)s narrow a list. `Tabs` only records which tab is chosen, so show each tab's content with an `<If>`:

```xml
<Tabs value="{{ tab }}" variant="segmented" onChange="setTab">
  <Tab value="sent" label="Sent" />
  <Tab value="received" label="Received" badge="{{ unread }}" />
</Tabs>
<If condition="{{ tab === 'sent' }}">
  <Text>Money you sent</Text>
</If>
<Else>
  <Text>Money you received</Text>
</Else>
```

A `Tab` can also have an `icon`, which shows before its label. A `disabled` `Tab` can't be chosen.

### A page's own navigation

For tabs that stay on screen, put `Tabs` with `variant="bar"` in a [`Pinned`](/docs/products/dash-dots/reference/components/pinned). The tabs share the width equally, with each icon over its label and each `badge` on its icon's corner:

```xml
<View>
  <If condition="{{ tab === 'home' }}">
    <Text>Today's specials</Text>
  </If>
  <ElseIf condition="{{ tab === 'orders' }}">
    <Text>Your orders</Text>
  </ElseIf>
  <Else>
    <Text>Your account</Text>
  </Else>

  <Pinned edge="bottom" border="subtle">
    <Tabs variant="bar" value="{{ tab }}" onChange="setTab">
      <Tab value="home" label="Home" icon="home" />
      <Tab value="orders" label="Orders" icon="receipt" badge="{{ newOrders }}" />
      <Tab value="account" label="Account" icon="user" />
    </Tabs>
  </Pinned>
</View>
```

![Three tabs along the bottom of the screen, each with an icon over its label](/assets/images/dash-dots/components/pinned.webp)

Set `tabBar: { hidden: true }` in the page's [`configs`](/docs/products/dash-dots/developers/navigation#dashs-bars), so Dash's tab bar doesn't show under yours.

## Feedback

| Component                                                                   | Use it for                                |
| --------------------------------------------------------------------------- | ----------------------------------------- |
| [`Spinner`](/docs/products/dash-dots/reference/components/spinner)          | Something's loading                       |
| [`Skeleton`](/docs/products/dash-dots/reference/components/skeleton)        | A placeholder shaped like what's loading  |
| [`ProgressBar`](/docs/products/dash-dots/reference/components/progress-bar) | How far along an upload is, from 0 to 100 |
| [`Notice`](/docs/products/dash-dots/reference/components/notice)            | A message that stays on the page          |
| [`EmptyState`](/docs/products/dash-dots/reference/components/empty-state)   | What to show when there's nothing yet     |

```xml
<EmptyState icon="receipt" title="No payments yet" text="Payments you make show here.">
  <Button onClick="pay">Make a payment</Button>
</EmptyState>
```

## Sheets and toasts

A [`Sheet`](/docs/products/dash-dots/reference/components/sheet) slides up over the page. A [`Toast`](/docs/products/dash-dots/reference/components/toast) shows a quick message, like "Copied". Both follow the state:

- A `Sheet` shows while `open` is true. Tapping outside it sends `close`, and your handler sets `open` back to false.
- A `Toast` shows its `message`, then sends `hide`. Clear the message there so it can show again.

```xml
<Sheet open="{{ selected !== null }}" title="Payment" onClose="closeSheet">
  <Button variant="secondary" onClick="copyReference">Copy reference</Button>
  <Button onClick="closeSheet">Cancel</Button>
</Sheet>
<Toast message="{{ toast }}" tone="success" onHide="clearToast" />
```
