Expo UI and Router for iOSExpo SDK 57 and later, iOS only
Components

Button

A SwiftUI Button component for displaying native buttons.

Expo UI Button matches the official SwiftUI Button API and supports styling via the buttonStyle, controlSize, and other modifiers.

Usage

Basic button

import { Host, Button } from '@expo/ui/swift-ui';

export default function BasicButtonExample() {
  return (
    <Host style={{ flex: 1 }}>
      <Button label="Press me" onPress={() => alert('Pressed!')} />
    </Host>
  );
}
A blue Press me button
A blue Press me button

Button with system image

import { Host, Button } from '@expo/ui/swift-ui';

export default function ButtonWithImageExample() {
  return (
    <Host style={{ flex: 1 }}>
      <Button
        label="Download"
        systemImage="arrow.down.circle"
        onPress={() => alert('Downloading...')}
      />
    </Host>
  );
}
A Download button with a downward arrow icon before the label
A Download button with a downward arrow icon before the label

Icon-only button

Use the labelStyle modifier to show only the icon while keeping the label for accessibility.

import { Host, Button } from '@expo/ui/swift-ui';
import { labelStyle } from '@expo/ui/swift-ui/modifiers';

export default function IconOnlyButtonExample() {
  return (
    <Host style={{ flex: 1 }}>
      <Button
        label="Settings"
        systemImage="gear"
        modifiers={[labelStyle('iconOnly')]}
        onPress={() => alert('Settings')}
      />
    </Host>
  );
}
A gear icon button with no visible label
A gear icon button with no visible label

Button styles

Use the buttonStyle modifier to change the button's appearance. Available styles are: bordered, borderedProminent, borderless, plain, glass, and glassProminent.

Note: The glass and glassProminent styles are only available on iOS 26+ when built with Xcode 26.

import { Host, Button, VStack } from '@expo/ui/swift-ui';
import { buttonStyle } from '@expo/ui/swift-ui/modifiers';

export default function ButtonStylesExample() {
  return (
    <Host style={{ flex: 1 }}>
      <VStack spacing={8}>
        <Button
          label="Bordered"
          modifiers={[buttonStyle('bordered')]}
        />
        <Button
          label="Bordered Prominent"
          modifiers={[buttonStyle('borderedProminent')]}
        />
        <Button
          label="Borderless"
          modifiers={[buttonStyle('borderless')]}
        />
        <Button label="Plain" modifiers={[buttonStyle('plain')]} />
      </VStack>
    </Host>
  );
}
Four buttons showing the bordered, bordered prominent, borderless, and plain styles
Four buttons showing the bordered, bordered prominent, borderless, and plain styles

Button border shape

Use the buttonBorderShape modifier to change the shape of a styled button. Available shapes are: automatic, capsule, roundedRectangle, and circle (iOS 17+).

import { Host, Button } from '@expo/ui/swift-ui';
import {
  buttonStyle,
  controlSize,
  buttonBorderShape,
  labelStyle,
} from '@expo/ui/swift-ui/modifiers';

export default function ButtonBorderShapeExample() {
  return (
    <Host style={{ flex: 1 }}>
      <Button
        label="Favorite"
        systemImage="heart.fill"
        modifiers={[
          buttonStyle('glass'),
          controlSize('extraLarge'),
          labelStyle('iconOnly'),
          buttonBorderShape('circle'),
        ]}
        onPress={() => alert('Favorited')}
      />
    </Host>
  );
}
A circular glass button holding a heart icon
A circular glass button holding a heart icon

Control sizes

Use the controlSize modifier to adjust the button size. Available sizes are: mini, small, regular, large, and extraLarge.

Note: The extraLarge size is only available on iOS 17+.

import { Host, Button, VStack } from '@expo/ui/swift-ui';
import {
  buttonStyle,
  controlSize,
} from '@expo/ui/swift-ui/modifiers';

export default function ControlSizeExample() {
  return (
    <Host style={{ flex: 1 }}>
      <VStack spacing={8}>
        <Button
          label="Mini"
          modifiers={[controlSize('mini'), buttonStyle('bordered')]}
        />
        <Button
          label="Small"
          modifiers={[
            controlSize('small'),
            buttonStyle('bordered'),
          ]}
        />
        <Button
          label="Regular"
          modifiers={[
            controlSize('regular'),
            buttonStyle('bordered'),
          ]}
        />
        <Button
          label="Large"
          modifiers={[
            controlSize('large'),
            buttonStyle('bordered'),
          ]}
        />
      </VStack>
    </Host>
  );
}
Four bordered buttons at the mini, small, regular, and large sizes
Four bordered buttons at the mini, small, regular, and large sizes

Button roles

Use the role prop to indicate the semantic role of the button. Available roles are: default, cancel, and destructive.

import { Host, Button, VStack } from '@expo/ui/swift-ui';

export default function ButtonRolesExample() {
  return (
    <Host style={{ flex: 1 }}>
      <VStack spacing={8}>
        <Button label="Default" role="default" />
        <Button label="Cancel" role="cancel" />
        <Button label="Delete" role="destructive" />
      </VStack>
    </Host>
  );
}
Three buttons showing the default, cancel, and red destructive roles
Three buttons showing the default, cancel, and red destructive roles

Tinted button

Use the tint modifier to change the button's color.

import { Host, Button } from '@expo/ui/swift-ui';
import { tint } from '@expo/ui/swift-ui/modifiers';

export default function TintedButtonExample() {
  return (
    <Host style={{ flex: 1 }}>
      <Button label="Custom Color" modifiers={[tint('#FF6347')]} />
    </Host>
  );
}
A Custom Color button tinted tomato red
A Custom Color button tinted tomato red

Disabled button

Use the disabled modifier to disable the button.

import { Host, Button } from '@expo/ui/swift-ui';
import { disabled } from '@expo/ui/swift-ui/modifiers';

export default function DisabledButtonExample() {
  return (
    <Host style={{ flex: 1 }}>
      <Button label="Disabled" modifiers={[disabled()]} />
    </Host>
  );
}
A grayed out Disabled button
A grayed out Disabled button

Custom label content

You can pass custom components as children for more complex button label content.

import {
  Host,
  Button,
  VStack,
  Image,
  Text,
} from '@expo/ui/swift-ui';

export default function CustomContentExample() {
  return (
    <Host style={{ flex: 1 }}>
      <Button onPress={() => console.log('Pressed!')}>
        <VStack spacing={4}>
          <Image systemName="folder" />
          <Text>Folder</Text>
        </VStack>
      </Button>
    </Host>
  );
}
A button with a folder icon stacked above the label Folder
A button with a folder icon stacked above the label Folder

API

import { Button } from '@expo/ui/swift-ui';

Component

Button

Type: React.Element<ButtonProps>

Displays a native button component.

Example

import { Button } from '@expo/ui/swift-ui';
import { buttonStyle, controlSize, tint, disabled } from '@expo/ui/swift-ui/modifiers';

<Button
  role="destructive"
  onPress={handlePress}
  label="Delete"
  modifiers={[
    buttonStyle('bordered'),
    controlSize('large'),
    tint('#FF0000'),
    disabled(true)
  ]}
/>

ButtonProps

children

Optional • Literal type: union

Custom content for the button label. Use this for custom label views. Only nested elements are supported, not plain strings.

Acceptable values are: ReactElement<unknown, string | JSXElementConstructor<any>> | ReactElement[]

label

Optional • Type: string

The text label for the button. Use this for simple text buttons.

onPress

Optional • Type: () => void

A callback that is called when the button is pressed.

role

Optional • Type: ButtonRole

Indicates the role of the button.

systemImage

Optional • Type: SFSymbols7_0

A string describing the system image to display in the button. Only used when label is provided.

target

Optional • Type: string

Target identifier for the button, used for identifying which button was pressed in widgets and live activities.

Inherited props

Types

ButtonRole

Literal type: string

The role of the button.

  • default - The default button role.
  • cancel - A button that cancels the current operation.
  • destructive - A button that deletes data or performs a destructive action.

Acceptable values are: 'default' | 'cancel' | 'destructive'

Modifiers

Use these modifiers in the modifiers prop of the SwiftUI and Universal version.

Expo UI lets you put any of the 89 modifiers for any view on Button, but not all of them do something here.

  • 70 change what you see on Button.
  • 6 have no visible effect on Button.
  • 13 work only in one place, such as a widget or a Grid.

Best modifiers for Button

These modifiers are made for Button, or they are the ones that you use most with it.

Also works on Button

Button takes these modifiers too, and each one works in the usual way.

No visible effect on Button

Button accepts these modifiers, but you see no change. They do not give an error.

ModifierWhy nothing happens
accessibilityElementIt joins the child views into one accessibility element, and Button takes no child views.
containerShapeIt sets the shape that the child views use for containerRelativeShape, and Button takes no child views.
geometryGroup iOS 17+It works on the child views that you give the component, and Button takes none.
layoutPriorityIt has an effect only when the view shares an axis with other views in a stack.
onTapGestureButton handles taps itself. Use its onPress prop. A tap gesture on a button can stop the button action.
zIndexIt changes the order of views that cover each other in the same parent, and Button does not stack its children.

Only in some places

These modifiers work only in a widget, in a Grid, in a Namespace or in a parent that asks for them.

ModifierWhere it works
activityBackgroundTintWorks only in a Live Activity. It has no effect in the app.
alignmentGuideHas an effect only when the parent, such as a VStack or ZStack, aligns its children by the same guide.
containerBackgroundWorks only in a widget, or in a navigation container when you use a navigation placement.
glassEffectIdWorks only on a view that also has glassEffect, in a GlassEffectContainer and a Namespace.
gridCellAnchor iOS 16+Works only on a cell of a Grid. Grid is in @expo/ui/swift-ui, but it has no Expo docs page.
gridCellColumnsWorks only on a view in a Grid.Row. Grid is in @expo/ui/swift-ui, but it has no Expo docs page.
gridCellUnsizedAxesWorks only on a view in a Grid. Grid is in @expo/ui/swift-ui, but it has no Expo docs page.
gridColumnAlignment iOS 16+Works only on a view in a Grid.Row. Grid is in @expo/ui/swift-ui, but it has no Expo docs page.
invalidatableContent iOS 17+Has an effect only when a parent view uses redacted with the invalidated reason.
matchedGeometryEffectWorks only in a Namespace. Give two views the same id and change between them with an animation.
privacySensitiveHas an effect only when a parent view uses redacted with the privacy reason.
unredactedHas an effect only in a parent view that uses redacted.
widgetURLWorks only in a widget. It has no effect in the app.

Sources

The original Expo docs for this page:

On this page