SwiftUI.cc
Snippets
Text & InputText EntryIntermediate

SwiftUI TextEditor Multiline Editing

TextEditor binds long-form scrolling text. Style with font and lineSpacing, restyle backgrounds via scrollContentBackground, and manage the keyboard with focus and toolbars.

4 min readUpdated 2026-06
Notes editor with placeholder and styling
import SwiftUI

struct NotesEditor: View {
    @State private var note = ""
    @FocusState private var editing: Bool

    var body: some View {
        ZStack(alignment: .topLeading) {
            TextEditor(text: $note)
                .font(.body)
                .lineSpacing(6)
                .scrollContentBackground(.hidden)
                .background(.yellow.opacity(0.12))
                .focused($editing)

            if note.isEmpty {
                Text("Write something…")
                    .foregroundStyle(.tertiary)
                    .padding(.top, 10)
                    .padding(.leading, 6)
                    .allowsHitTesting(false)
            }
        }
        .toolbar {
            ToolbarItemGroup(placement: .keyboard) {
                Spacer()
                Button("Done") { editing = false }
            }
        }
        .padding()
    }
}

TextEditor preview

A scrolling canvas for prose

TextEditor(text: $note) owns scrolling, wrapping, selection, and editing for paragraph-length content. Typography modifiers apply to all content uniformly — font, lineSpacing, multilineTextAlignment, kerning — since the editor is plain-text by binding.

The background dance

Like List, the editor paints its own background that blocks yours until hidden:

TextEditor(text: $note)
    .scrollContentBackground(.hidden)
    .background(.yellow.opacity(0.12), in: .rect(cornerRadius: 12))

Placeholder pattern

No placeholder parameter exists, so overlay one: a ZStack(alignment: .topLeading) with a tertiary-styled Text shown while the binding is empty, marked allowsHitTesting(false) so taps reach the editor. Match the editor's small internal insets (~5–8 points) so the placeholder aligns with the eventual cursor.

Keyboard management

Long editors live under the keyboard's shadow. The toolkit: FocusState to resign on Done, a .keyboard-placement toolbar for the button itself, and scrollDismissesKeyboard(.interactively) so dragging the content down peels the keyboard away, Messages-style. findNavigator(isPresented:) adds system find/replace for document-scale editing.

Common mistakes

  • Forgetting scrollContentBackground and concluding backgrounds are broken.
  • Placeholders that intercept the first tap.
  • Using TextEditor in a Form row where a growing TextField was the design intent.

Related reference