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):

import SwiftUI

struct ParentView: View {
    @State private var count = 0

    var body: some View {
        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.

import SwiftUI

struct ChildView: View {
    @Binding var count: Int

    var body: some View {
        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

import SwiftUI

struct SettingsView: View {
    @State private var notificationsOn = false

    var body: some View {
        NotificationToggle(isOn: $notificationsOn)
            .padding()
    }
}

struct NotificationToggle: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle("Notifications", isOn: $isOn)
    }
}

Example 2: Text field component

import SwiftUI

struct ProfileForm: View {
    @State private var username = ""

    var body: some View {
        UsernameField(username: $username)
            .padding()
    }
}

struct UsernameField: View {
    @Binding var username: String

    var body: some View {
        TextField("Username", text: $username)
            .textFieldStyle(.roundedBorder)
    }
}

Example 3: Counter control

import SwiftUI

struct Dashboard: View {
    @State private var score = 0

    var body: some View {
        ScoreControl(score: $score)
            .padding()
    }
}

struct ScoreControl: View {
    @Binding var score: Int

    var body: some View {
        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:

struct ChildView: View {
    @State var count: Int
}

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

Correct approach:

struct ChildView: View {
    @Binding var count: 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.