SwiftUI Tutorial · iOS 16+ · iOS 26 Liquid Glass

SwiftUI TextField: Focus, Validation & SecureField

Build real forms in SwiftUI — bindings, keyboard types, @FocusState field navigation, live validation, secure entry, and multiline text that grows with the content.

Last updated: 2026-07-17 10 min read By Ahmed Gagan, iOS Engineer
Quick Answer

A SwiftUI TextField binds a String to editable text: TextField("Email", text: $email). Configure the keyboard with .keyboardType, control focus with a @FocusState property and the .focused modifier, and set the return key with .submitLabel. For passwords use SecureField, and for growing multiline input use TextField("Notes", text: $notes, axis: .vertical) available since iOS 16.

Core binding
TextField(_:text:) with a String Binding
Focus control
@FocusState + .focused(_:equals:)
Multiline (iOS 16+)
TextField(text:axis: .vertical)
Swift Kit support
The Swift Kit's auth forms ship validated email/password fields with focus chaining out of the box.

Bindings and keyboard configuration

A TextField edits a value through a Binding, most often a String. Choose the right keyboard for the data with .keyboardType — .emailAddress for emails, .numberPad for PINs — and pair it with .textInputAutocapitalization and .autocorrectionDisabled so the field does not fight the user. For anything other than free text, disabling autocorrect prevents frustrating substitutions. These modifiers are cheap and dramatically improve the typing experience, so set them deliberately on every field.

An email field configured correctly
@State private var email = ""

TextField("Email address", text: $email)
    .keyboardType(.emailAddress)
    .textContentType(.emailAddress)
    .textInputAutocapitalization(.never)
    .autocorrectionDisabled()
    .textFieldStyle(.roundedBorder)

Managing focus with @FocusState

@FocusState lets you read and control which field is active. Declare a state property — often an enum of your fields — and attach .focused(_:equals:) to each TextField. You can then move focus programmatically, for example advancing to the next field when the user taps return, or dismissing the keyboard by setting focus to nil. This is the correct, first-party way to chain fields; avoid UIKit responder hacks now that @FocusState exists.

Chaining fields on submit
enum Field { case email, password }

struct LoginForm: View {
    @State private var email = ""
    @State private var password = ""
    @FocusState private var focus: Field?

    var body: some View {
        VStack {
            TextField("Email", text: $email)
                .focused($focus, equals: .email)
                .submitLabel(.next)
            SecureField("Password", text: $password)
                .focused($focus, equals: .password)
                .submitLabel(.go)
        }
        .onSubmit {
            focus = focus == .email ? .password : nil
        }
    }
}

Live validation

Validate as the user types by deriving a computed property from the bound value and reacting to it. A common pattern is to show an inline error only after the field has been touched, so you don't scold the user before they have finished typing. Drive the submit button's .disabled state from the same computed validity. Keep validation logic in the view model when it grows, but for a single field a computed Bool in the view is perfectly clean.

Inline email validation
@State private var email = ""

private var isValidEmail: Bool {
    email.contains("@") && email.contains(".")
}

var body: some View {
    VStack(alignment: .leading, spacing: 4) {
        TextField("Email", text: $email)
            .textFieldStyle(.roundedBorder)
        if !email.isEmpty && !isValidEmail {
            Text("Enter a valid email address")
                .font(.caption)
                .foregroundStyle(.red)
        }
    }
}

SecureField for passwords

SecureField mirrors TextField but masks the input, and it is what you should use for passwords and other secrets. Set .textContentType(.password) or .newPassword so the system offers Passwords app autofill and strong-password suggestions. A frequent request is a reveal toggle; implement it by conditionally swapping between SecureField and TextField backed by the same binding, so the entered text is preserved when the user flips visibility.

Password field with a reveal toggle
@State private var password = ""
@State private var isRevealed = false

HStack {
    Group {
        if isRevealed {
            TextField("Password", text: $password)
        } else {
            SecureField("Password", text: $password)
        }
    }
    .textContentType(.password)
    Button {
        isRevealed.toggle()
    } label: {
        Image(systemName: isRevealed ? "eye.slash" : "eye")
    }
}

Multiline text and custom styles

Since iOS 16, TextField grows vertically when you pass axis: .vertical, replacing most uses of the older TextEditor for short-to-medium input. Constrain its growth with .lineLimit(_:reservesSpace:) so it starts at a sensible height and expands to a cap. For styling, .textFieldStyle(.roundedBorder) covers most needs, but you can build a custom look by conforming to TextFieldStyle. On iOS 26, wrapping fields in a .glassEffect() container gives inputs the Liquid Glass treatment consistent with the rest of the system.

A growing multiline notes field
@State private var notes = ""

TextField("Notes", text: $notes, axis: .vertical)
    .lineLimit(3...8)          // starts at 3 lines, grows to 8
    .textFieldStyle(.roundedBorder)
    .submitLabel(.return)

Auth forms, already validated

The Swift Kit's Supabase sign-in and sign-up screens ship with validated email/password fields, focus chaining, and autofill wired up — no form boilerplate.

Get The Swift Kit — $99

How to dismiss the keyboard in SwiftUI

Give users a reliable way to close the keyboard from a TextField.

  1. 1

    Declare focus state

    Add a @FocusState property to track the active field.

    @FocusState private var isFocused: Bool
  2. 2

    Bind the field

    Attach .focused to the TextField so SwiftUI can track and change focus.

    TextField("Search", text: $query).focused($isFocused)
  3. 3

    Add a Done button

    Put a toolbar button above the keyboard that sets focus to false.

    .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer(); Button("Done") { isFocused = false } } }

Frequently Asked Questions

How do I move focus to the next TextField in SwiftUI?
Use a @FocusState property, typically an enum of your fields, and attach .focused(_:equals:) to each TextField. In .onSubmit, set the focus state to the next case to advance, or to nil to dismiss the keyboard.
How do I make a multiline TextField in SwiftUI?
Since iOS 16 pass axis: .vertical, as in TextField("Notes", text: $notes, axis: .vertical). Combine it with .lineLimit(3...8) so the field starts at a minimum height and grows up to a cap as the user types.
How do I add a show/hide password toggle to SecureField in SwiftUI?
Keep both a SecureField and a TextField bound to the same @State string and swap between them with an isRevealed flag toggled by an eye button. Because they share the binding, the typed text is preserved when visibility changes.
How do I validate a TextField in SwiftUI as the user types?
Derive a computed Bool from the bound value and use it to show an inline error and to drive the submit button's .disabled state. Only show the error after the field is non-empty so you don't warn the user prematurely.
How do I set the return key label on a SwiftUI TextField?
Apply .submitLabel with a value such as .next, .go, .search, or .done. Handle the action in .onSubmit, for example moving focus to the next field or triggering a submit.

Keep exploring

Ship the boring parts on day one

The Swift Kit is a $99 one-time SwiftUI boilerplate with auth forms, validation, paywalls, and AI ready to go — lifetime updates and a 14-day refund.

Get The Swift Kit — $99

One-time purchase · Lifetime updates · 14-day refund