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

Button with system image

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>
);
}
Button styles
Use the buttonStyle modifier to change the button's appearance. Available styles are: bordered, borderedProminent, borderless, plain, glass, and glassProminent.
Note: The
glassandglassProminentstyles 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>
);
}
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>
);
}
Control sizes
Use the controlSize modifier to adjust the button size. Available sizes are: mini, small, regular, large, and extraLarge.
Note: The
extraLargesize 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>
);
}
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>
);
}
Tinted button
Use the tint modifier to change the button's color.

Disabled button
Use the disabled modifier to disable the 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>
);
}
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.
accessibilityAddTraitsaccessibilityHiddenaccessibilityHintaccessibilityIdentifieraccessibilityInputLabelsaccessibilityLabelaccessibilityRemoveTraitsaccessibilityValueallowsTighteninganimationaspectRatiobackgroundbackgroundOverlayblurboldborderbrightnessclippedclipShapecolorInvertcontainerRelativeFrameiOS 17+contentShapecontentTransitioniOS 16+contrastcornerRadiusdynamicTypeSizeenvironmentfixedSizefontforegroundColorforegroundStylegrayscalehiddenhueRotationidignoreSafeAreaimageScaleitalickerninglineHeightiOS 26+lineLimitlineSpacingluminanceToAlphamaskminimumScaleFactormonospacedDigitmultilineTextAlignmentoffsetonAppearonDisappearonGeometryChangeonLongPressGestureopacityoverlayredactedrotation3DEffectrotationEffectsaturationscaleEffectshadowstrikethroughstrokeBordertextCasetruncationModeunderline
No visible effect on Button
Button accepts these modifiers, but you see no change. They do not give an error.
| Modifier | Why nothing happens |
|---|---|
accessibilityElement | It joins the child views into one accessibility element, and Button takes no child views. |
containerShape | It 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. |
layoutPriority | It has an effect only when the view shares an axis with other views in a stack. |
onTapGesture | Button handles taps itself. Use its onPress prop. A tap gesture on a button can stop the button action. |
zIndex | It 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.
| Modifier | Where it works |
|---|---|
activityBackgroundTint | Works only in a Live Activity. It has no effect in the app. |
alignmentGuide | Has an effect only when the parent, such as a VStack or ZStack, aligns its children by the same guide. |
containerBackground | Works only in a widget, or in a navigation container when you use a navigation placement. |
glassEffectId | Works 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. |
gridCellColumns | Works only on a view in a Grid.Row. Grid is in @expo/ui/swift-ui, but it has no Expo docs page. |
gridCellUnsizedAxes | Works 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. |
matchedGeometryEffect | Works only in a Namespace. Give two views the same id and change between them with an animation. |
privacySensitive | Has an effect only when a parent view uses redacted with the privacy reason. |
unredacted | Has an effect only in a parent view that uses redacted. |
widgetURL | Works only in a widget. It has no effect in the app. |
Sources
The original Expo docs for this page:
- Button (SwiftUI) (
source-of-truth/versions/v57.0.0/sdk/ui/swift-ui/button.md) - Button (Universal) (
source-of-truth/versions/v57.0.0/sdk/ui/universal/button.md)









