SwiftUI.cc
Snippets
ControlsButtonsBeginner

SwiftUI Button Roles, Styles, and Repeat Behavior

Button pairs an action with any label. Roles mark destructive and cancel semantics, built-in styles cover most designs, and buttonRepeatBehavior auto-repeats while held.

4 min readUpdated 2026-06
Action row with roles and prominence
import SwiftUI

struct DocumentActions: View {
    var body: some View {
        VStack(spacing: 12) {
            Button {
                // save
            } label: {
                Label("Save Document", systemImage: "tray.and.arrow.down")
                    .frame(maxWidth: .infinity)
            }
            .buttonStyle(.borderedProminent)
            .controlSize(.large)

            Button("Duplicate") { }
                .buttonStyle(.bordered)

            Button("Delete", role: .destructive) { }
                .buttonStyle(.bordered)
        }
        .padding()
    }
}

Button styles preview

Anatomy

A button is an action plus a label view. The label can be any view — Label, an image-and-text stack, a custom badge — and the closure-label initializer keeps complex labels readable.

Roles carry meaning

Button("Delete", role: .destructive) { delete() }
Button("Cancel", role: .cancel) { }

Inside Menu, confirmationDialog, and alert, roles drive the system's placement and coloring — cancel buttons get their standard slot, destructive ones turn red. Using roles instead of manual .foregroundStyle(.red) keeps behavior consistent everywhere the button appears.

Styles and sizing

The built-in styles express prominence levels. .borderedProminent + controlSize(.large) + frame(maxWidth: .infinity) is the standard primary CTA recipe; .bordered siblings read as secondary. tint recolors a style without redesigning it.

Repeat behavior

buttonRepeatBehavior(.enabled) turns press-and-hold into repeated action firing — exactly what volume nudgers, quantity steppers, and scrubbers want, with system-tuned acceleration instead of a custom timer.

Common mistakes

  • Three prominent buttons in one view, so nothing is actually prominent.
  • Wrapping navigation in Buttons that mutate state instead of using NavigationLink values.
  • Tiny icon-only buttons without padding, failing the 44-point target.

Related reference