SwiftUI.cc
Snippets
State UIPreviewsBeginner

SwiftUI #Preview Macro and Preview Traits

#Preview declares canvas previews with names, traits like landscape orientation and size fitting, plus @Previewable for stateful previews — fast iteration without running the app.

4 min readUpdated 2026-06
Named previews with traits and state
import SwiftUI

struct VolumeRow: View {
    @Binding var volume: Double

    var body: some View {
        HStack {
            Image(systemName: "speaker.wave.2")
            Slider(value: $volume)
            Text(volume, format: .percent.precision(.fractionLength(0)))
                .monospacedDigit()
        }
        .padding()
    }
}

#Preview("Interactive") {
    @Previewable @State var volume = 0.4
    VolumeRow(volume: $volume)
}

#Preview("Dark, fitted", traits: .sizeThatFitsLayout) {
    VolumeRow(volume: .constant(0.7))
        .preferredColorScheme(.dark)
}

#Preview("Landscape", traits: .landscapeLeft) {
    VolumeRow(volume: .constant(0.7))
}

Preview macro preview

The inner loop

#Preview { MyView() } registers a canvas preview compiled alongside your code. Multiple named previews per file act as a living spec: default, dark, large text, edge-case data — visible simultaneously in the canvas picker.

State that works

Previews are static by default; bindings need real storage. @Previewable provides it inline:

#Preview {
    @Previewable @State var isOn = true
    Toggle("Wi-Fi", isOn: $isOn)
}

The macro hoists that state into a generated wrapper — the manual 'PreviewHost' struct pattern, automated.

Traits vs modifiers

Traits configure the canvas: orientation (.landscapeLeft), layout (.sizeThatFitsLayout to hug the component instead of a full device). Environment modifiers configure the view: preferredColorScheme(.dark), dynamicTypeSize(.accessibility3), environment(\.locale, Locale(identifier: "de")). Combining both pins exact variants; the canvas's built-in variant modes (Dynamic Type, color scheme grids) cover the exploratory sweep.

Fixtures over singletons

Previews reward dependency injection: a view taking a model value previews with three sample values trivially; one reaching into shared services drags the world along. Keep sample data factories next to your models — previews are their first consumer.

Common mistakes

  • .constant bindings everywhere, leaving controls dead in the canvas.
  • Only previewing the happy path at default size.
  • Heavy app-launch work imported transitively, making 'previews are slow' a self-inflicted wound.

Related reference