# Buttons and inputs

> **Info:** This is release candidate documentation for **Stream Chat React Native SDK v10**. For the latest stable version, see the [latest version (v9)](https://getstream.io/chat/docs/sdk/react-native/).

The SDK draws its buttons and text fields with a few shared components. Using them in your own screens keeps the colors, sizes, press states and right-to-left behavior consistent with the SDK. Every component on this page is exported from `stream-chat-react-native` and `stream-chat-expo`, and none has an override key.

## Best practices

- Give an icon-only `Button` an accessibility label, with `accessibilityLabel` or `accessibilityLabelKey`. It has no text to announce.
- `Button` fills the width of its container unless it's `iconOnly`. Pass `style={{ width: "auto" }}` for a button as wide as its label.
- Pass icons as `LeadingIcon` and `TrailingIcon`, not inside `label`. The button sizes and colors them, and swaps them in a right-to-left layout.
- Keep `Input`'s `state` and messages in your own state, and set `state="error"` with an `errorMessage` when validation fails. On Android, TalkBack announces the message.
- Every component here reads the theme, which `OverlayProvider` and `Chat` provide.

## Available components

| Component             | What it is                                                   | Where the SDK renders it                                                                              |
| --------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| `Button`              | A pressable pill with a label, icons, or both                | The composer's buttons, the message menu, the image gallery, notifications, polls and channel details |
| `Input`               | A text field with an optional title, icons and a helper line | `SearchInput`, the channel name in the edit form, and the poll creation form                          |
| `SearchInput`         | An `Input` with a search icon and a clear button             | The member list, the add members form and the pinned messages list of channel details                 |
| `SpeedSettingsButton` | The playback rate, such as `x1.5`                            | Audio attachments and the image gallery's video controls                                              |
| `GiphyChip`           | The composer's active command, with a button that clears it  | The composer's text field, while a command such as `/giphy` is set                                    |

## Button

`Button` is a React Native Gesture Handler `Pressable` inside a rounded `View`. `variant` and `type` pick its colors from the `button*` semantic tokens:

| `type`    | Looks like                                     |
| --------- | ---------------------------------------------- |
| `solid`   | Filled with the variant's background color     |
| `outline` | A 1-point border in the variant's border color |
| `ghost`   | No background and no border                    |

A pressed button gets `semantics.backgroundUtilityPressed`, and a `selected` one `semantics.backgroundUtilitySelected`. A disabled one uses the disabled colors.

`ButtonProps` are the props of the Gesture Handler `Pressable`, which go to it, plus:

| Prop                       | Type                                               | Default  | Description                                                                                                |
| -------------------------- | -------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------- |
| `variant`                  | `'primary' \| 'secondary' \| 'destructive'`        | required | The color set.                                                                                             |
| `type`                     | `'solid' \| 'outline' \| 'ghost' \| 'liquidGlass'` | required | The fill. `'liquidGlass'` has no styles, and a button of that type throws when it renders.                 |
| `size`                     | `'sm' \| 'md' \| 'lg'`                             | `'md'`   | 32, 40 or 48 points tall. An `iconOnly` button is as wide as it's tall.                                    |
| `label`                    | `React.ReactNode`                                  |          | The content. A string is wrapped in a `Text` in the button's color; other nodes render as they are.        |
| `LeadingIcon`              | `IconRenderer`                                     |          | The icon before the label. It gets a 20-point size, a 1.5 stroke width and the button's color as `stroke`. |
| `TrailingIcon`             | `IconRenderer`                                     |          | The icon after the label. The two swap sides in a right-to-left layout.                                    |
| `iconOnly`                 | `boolean`                                          | `false`  | Renders only `LeadingIcon`, or `TrailingIcon` without one, in a square button.                             |
| `selected`                 | `boolean`                                          | `false`  | Shows the selected background and reports the selected state to screen readers.                            |
| `accessibilityLabelKey`    | `string`                                           |          | A translation key for the label. With SDK accessibility on, its translation replaces `accessibilityLabel`. |
| `accessibilityLabelParams` | `Record<string, unknown>`                          |          | The values for `accessibilityLabelKey`.                                                                    |
| `style`                    | `StyleProp<ViewStyle>`                             |          | Applied to the outer `View`, after the size.                                                               |

`disabled` and `onLayout` come from `Pressable`. `disabled` defaults to `false`, and `onLayout` goes to the outer `View`. `IconRenderer` is `(props: IconProps) => React.ReactNode`, so the icons of `ComponentsContext` can be passed as they are.

## Input

`Input` is a React Native `TextInput` with a frame around it. It forwards its ref to the `TextInput`. Above the field it shows `title` and `description`, and below it a helper line that depends on `state`:

| `state`     | The helper line shows                                   |
| ----------- | ------------------------------------------------------- |
| `'default'` | An info icon and `infoText`                             |
| `'error'`   | An info icon and `errorMessage`, in the error color     |
| `'success'` | A check mark and `successMessage`, in the success color |

While `state` is `'error'`, `errorMessage` is also announced through an assertive live region, even with `helperText` off. `accessibilityLiveRegion` is Android only, so VoiceOver on iOS doesn't announce it. The border is `semantics.borderUtilityActive` while the field is focused and `semantics.borderUtilityDisabled` while it isn't `editable`.

`InputProps` are the props of `TextInput`, which go to it, plus:

| Prop             | Type                                | Default     | Description                                                                                                 |
| ---------------- | ----------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------- |
| `variant`        | `'outline' \| 'ghost'`              | required    | `'outline'` draws a 1-point border; `'ghost'` draws none.                                                   |
| `title`          | `string`                            |             | The label above the field. It's also the field's accessibility label, unless you pass `accessibilityLabel`. |
| `description`    | `string`                            |             | A line under the title. It's also the field's accessibility hint.                                           |
| `LeadingIcon`    | `IconRenderer`                      |             | An icon at the start of the field.                                                                          |
| `TrailingIcon`   | `IconRenderer`                      |             | An icon at the end of the field. The two swap sides in a right-to-left layout.                              |
| `state`          | `'default' \| 'error' \| 'success'` | `'default'` | Picks the helper line and its color.                                                                        |
| `helperText`     | `boolean`                           | `true`      | Shows the helper line.                                                                                      |
| `errorMessage`   | `string`                            |             | The helper text in the `'error'` state.                                                                     |
| `successMessage` | `string`                            |             | The helper text in the `'success'` state.                                                                   |
| `infoText`       | `string`                            |             | The helper text in the `'default'` state.                                                                   |
| `containerStyle` | `StyleProp<ViewStyle>`              |             | Applied to the frame around the field and its icons.                                                        |

`editable` defaults to `true`. A `style` you pass replaces the field's default text style instead of adding to it, so set the font size and color you need in it.

## SearchInput

`SearchInput` renders an `Input` with a search icon, the placeholder "Search", no helper line, words capitalized and autocorrect off. Once the user has typed, a clear button shows; it empties the field and calls `onChangeText("")`. It sits in a `View` with padding on every side.

`SearchInputProps` is `Partial<InputProps>`. Every prop you pass goes to the `Input` and replaces the default, except `onChangeText`, which `SearchInput` wraps to track whether the field has text.

## SpeedSettingsButton

`SpeedSettingsButton` shows the rate with one decimal, such as `x1.0` or `x1.5`, in a 40 by 24 point pill. It doesn't change the rate itself. Your `onPress` does.

| Prop                  | Type                   | Default  | Description                        |
| --------------------- | ---------------------- | -------- | ---------------------------------- |
| `currentPlaybackRate` | `number`               | required | The rate to show.                  |
| `onPress`             | `() => void`           | required | Called when the button is pressed. |
| `containerStyle`      | `StyleProp<ViewStyle>` |          | Applied to the pill.               |

The button has no accessibility label or role. Wrap it, or set them through a component of your own, when it must be reachable with a screen reader.

## GiphyChip

`GiphyChip` takes no props. It reads the composer from `useMessageComposer()`, so it renders inside a `Channel` or a `Thread`. It shows the name of the text composer's `command` in capitals, after a lightning icon, and a cross that calls `TextComposer.clearCommand()`. The composer's text field renders it while a command is set.

## Example

A form with a validated field, a search field, and a full-width and an icon-only button:

```tsx title="src/RenameForm.tsx"
import { useState } from "react";
import { View } from "react-native";
import {
  Button,
  Input,
  SearchInput,
  useComponentsContext,
} from "stream-chat-react-native";

export const RenameForm = ({
  onFilter,
  onSave,
}: {
  onFilter: (query: string) => void;
  onSave: (name: string) => void;
}) => {
  const { icons } = useComponentsContext();
  const [name, setName] = useState("");
  const tooLong = name.length > 40;

  return (
    <View style={{ gap: 12, padding: 16 }}>
      <SearchInput onChangeText={onFilter} placeholder="Find a channel" />
      <Input
        errorMessage="Use 40 characters or fewer"
        infoText="Everyone in the channel sees the name"
        onChangeText={setName}
        state={tooLong ? "error" : "default"}
        title="Channel name"
        value={name}
        variant="outline"
      />
      <View style={{ flexDirection: "row", gap: 8 }}>
        <Button
          disabled={!name || tooLong}
          label="Save"
          onPress={() => onSave(name)}
          style={{ flex: 1 }}
          type="solid"
          variant="primary"
        />
        <Button
          accessibilityLabel="Clear name"
          iconOnly
          LeadingIcon={icons.Cross}
          onPress={() => setName("")}
          type="outline"
          variant="secondary"
        />
      </View>
    </View>
  );
};
```

## Related

- [Overriding components](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/overriding-components/#icons): the icons of `ComponentsContext` and the `IconProps` they take.
- [Lists and indicators](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/base-ui/lists-and-indicators/): `EmptySearchResult`, which the SDK shows under `SearchInput` when nothing matches.
- [Accessibility](https://getstream.io/chat/docs/sdk/react-native/v10/features/app-experience/accessibility/): turning on the SDK's accessibility layer, which `accessibilityLabelKey` depends on.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/base-ui/buttons-and-inputs/](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/base-ui/buttons-and-inputs/).