Info:
This is release candidate documentation for Stream Chat React Native SDK v10. For the latest stable version, see the latest version (v9).

Buttons and inputs

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:

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>
  );
};
  • Overriding components: the icons of ComponentsContext and the IconProps they take.
  • Lists and indicators: EmptySearchResult, which the SDK shows under SearchInput when nothing matches.
  • Accessibility: turning on the SDK's accessibility layer, which accessibilityLabelKey depends on.