SwiftUI @State and @Binding explained
October 29, 2026 · 2 min read
Use @State in the view that owns a value, and @Binding in a child view that needs to change it. The parent passes its state down with a $ in front.
A quantity stepper
import SwiftUI
struct QuantityControl: View {
@Binding var quantity: Int
var body: some View {
Stepper("Quantity: \(quantity)", value: $quantity, in: 1...10)
}
}
struct DrinkView: View {
@State private var quantity = 1
var body: some View {
VStack(spacing: 16) {
Text("Latte × \(quantity)")
QuantityControl(quantity: $quantity)
}
.padding()
}
}
#Preview {
DrinkView()
}
Output (in the canvas or simulator): it starts as "Latte × 1" with "Quantity: 1". Tap + twice and both labels read 3. Tap − and they read 2. The stepper stops at 1 and at 10.
What is happening
@State private var quantity = 1makesDrinkViewthe owner. SwiftUI stores the value and runsbodyagain whenever it changes.@Binding var quantity: IntinQuantityControlstores no number. It is read and write access to a value someone else owns.$quantityinDrinkViewis the binding to that state. Passing it toQuantityControl(quantity:)hands the child that access.- Inside the child,
$quantitypasses the binding on toStepper, which needs it to change the value when you tap + or −.
There is only one copy of the number. When the stepper changes it, the parent's state changes, and both views redraw.
Common mistake
Using @State in the child. If QuantityControl declared @State private var quantity = 1, it would keep its own number. The stepper would work, but "Latte × 1" in the parent would never change. Declare the value once, in the owner, and use @Binding everywhere else.
Forgetting the $. Writing QuantityControl(quantity: quantity) passes a plain Int, and Xcode reports a type mismatch. Write $quantity to pass the binding.
Takeaway
One view owns the value with @State. Every child that needs to change it takes a @Binding, and you pass it with $.