How it works
ButtonRole is a structure that describes the semantic purpose of a button, letting you tell SwiftUI what an action *means* rather than only how it looks. You attach a role when a button performs a meaningful action such as deleting data or canceling an operation, and SwiftUI uses that meaning to style the button appropriately and to emphasize it correctly in containers like menus, alerts, and confirmation dialogs. Reach for ButtonRole whenever an action carries weight that the system should communicate consistently across platforms, instead of hand-coding colors or font weights for each control.
Pass a role to the Button initializer
Buttons accept an optional role through an initializer parameter, so you declare semantics at the point of creation. In the example,
Button("Delete", role: .destructive)andButton("Cancel", role: .cancel)carry roles, whileButton("Save")omits the argument and stays a neutral action.Use the .destructive role for irreversible actions
The
destructivestatic member marks a button whose action removes data or cannot easily be undone. SwiftUI renders it with prominent, typically red styling so the consequence is obvious — that's whyButton("Delete", role: .destructive)reads visually different from the plain Save button beside it.Use the .cancel role to dismiss without committing
The
cancelstatic member identifies the button that backs out of an operation. In dialogs and alerts SwiftUI gives it special placement and emphasis; hereButton("Cancel", role: .cancel)declares that intent even within an ordinaryVStack.Treat the role as optional semantics
Because the role parameter is an
Optional<ButtonRole>, a button without one — likeButton("Save")— is simply unmarked and styled as a standard action. You only supply a role when the action has a meaning the system should recognize.Combine roles with a button style
ButtonRole describes meaning; a style describes appearance, and the two compose. Applying
.buttonStyle(.bordered)to the container lets each role tint and weight itself within that shared style, so.destructiveand.cancelstay distinguishable while sharing the bordered look.
.confirmationDialog or Menu instead of the VStack to see how .destructive and .cancel get system-driven emphasis and ordering that a plain stack doesn't apply.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct ButtonRoleDemo: View {
var body: some View {
VStack(spacing: 16) {
Button("Delete", role: .destructive) {}
Button("Cancel", role: .cancel) {}
Button("Save") {}
}
.buttonStyle(.bordered)
.padding()
}
}