How to Use @Binding in SwiftUI

What you’ll build or solve

You’ll create a child SwiftUI view that can update a value without owning that state.

When this approach works best

@Binding works best when:

  • A parent view owns state and a child needs to edit it, like a form field or toggle row.
  • You are building reusable components that should not store their own copy of data.
  • You want to keep one source of truth while still letting nested views make updates.

This is a bad idea when the value should be owned and managed inside the child view. In that case, use @State in the child instead.

Prerequisites

  • Xcode with a SwiftUI project
  • You already have state owned by another view (commonly with @State)

Example parent state (for this guide):

importSwiftUI

structParentView:View {
@Stateprivatevarcount=0

varbody:someView {
ChildView(count: $count)
    }
}

Step-by-step instructions

Step 1: Declare a binding in the child view

Declare the value in the child using @Binding. The child does not own the data, it references it.

importSwiftUI

structChildView:View {
@Bindingvarcount:Int

varbody:someView {
Button("Increase") {
count+=1
        }
    }
}

What to look for

  • @Binding uses var, not let.
  • Updating count here updates the parent’s stored value.

Step 2: Pass a binding from the parent

Pass the binding when you create the child view.

ChildView(count: $count)

What to look for

  • Use $count to pass a binding, not count.
  • The binding type must match. Binding<Int> maps to @Binding var count: Int.

Examples you can copy

Example 1: Toggle row controlled by a parent

importSwiftUI

structSettingsView:View {
@StateprivatevarnotificationsOn=false

varbody:someView {
NotificationToggle(isOn: $notificationsOn)
            .padding()
    }
}

structNotificationToggle:View {
@BindingvarisOn:Bool

varbody:someView {
Toggle("Notifications",isOn: $isOn)
    }
}

Example 2: Text field component

importSwiftUI

structProfileForm:View {
@Stateprivatevarusername=""

varbody:someView {
UsernameField(username: $username)
            .padding()
    }
}

structUsernameField:View {
@Bindingvarusername:String

varbody:someView {
TextField("Username",text: $username)
            .textFieldStyle(.roundedBorder)
    }
}

Example 3: Counter control

importSwiftUI

structDashboard:View {
@Stateprivatevarscore=0

varbody:someView {
ScoreControl(score: $score)
            .padding()
    }
}

structScoreControl:View {
@Bindingvarscore:Int

varbody:someView {
HStack(spacing:12) {
Button("−") {score-=1 }
Text("\(score)")
Button("+") {score+=1 }
        }
    }
}

Common mistakes and how to fix them

Mistake 1: Passing a value instead of a binding

What you might do:

ChildView(count:count)

Why it breaks: The child expects a binding, but you passed an Int.

Correct approach:

ChildView(count: $count)

Mistake 2: Using @State in both parent and child

What you might do:

structChildView:View {
@Statevarcount:Int
}

Why it breaks: The child stores its own copy, so updates do not change the parent’s value.

Correct approach:

structChildView:View {
@Bindingvarcount:Int
}

Troubleshooting

  • If you see Cannot convert value of type 'Int' to expected argument type 'Binding<Int>', pass $count instead of count.
  • If the preview fails because a binding is missing, use a constant binding:
#Preview {
ChildView(count: .constant(0))
}
  • If updates do not show up, confirm you are not accidentally creating a second source of truth with @State in the child.
  • If types do not match, align them, for example @State var name: String pairs with @Binding var name: String.

Quick recap

  • Declare @Binding var value: Type in the child.
  • Pass the binding from the parent using $value.
  • Keep one source of truth by storing data in the parent and editing it in the child.
  • Match types exactly between @State and @Binding.
  • Use .constant(...) bindings for previews.