How it works
CustomizableToolbarContent is the protocol that toolbar items conform to when you want people to rearrange, add, or remove them. Where ordinary toolbar content is fixed at the positions you declare, customizable content participates in the system's toolbar editor: the user enters an edit mode, drags items between positions, and hides ones they don't need, and the system remembers their choices. You opt in to this behavior by giving your toolbar and its items stable identities, which is what lets SwiftUI persist a layout across launches. Reach for it when a screen exposes more actions than fit comfortably for everyone, and you'd rather let each person decide which ones stay visible.
Open a customizable toolbar with toolbar(id:)
The id form of the toolbar modifier is what turns its content into CustomizableToolbarContent. The string you pass is a stable key the system uses to store and restore the user's arrangement, so it must stay constant across launches. Here the toolbar is keyed with
toolbar(id: "main"), marking everything inside it as customizable rather than fixed.Give each item a stable identity with ToolbarItem(id:)
Inside a customizable toolbar, every item needs its own identifier so the system can track it as the user moves or hides it. ToolbarItem's id initializer supplies that identity; the example tags its three actions
ToolbarItem(id: "new", ...),ToolbarItem(id: "reply", ...), andToolbarItem(id: "flag", ...). Without these per-item ids, SwiftUI can't remember an individual item's customized position.Steer the default and optional slots with placement
Placement decides where an item starts before any customization and how prominent it is. Items declared with
placement: .primaryAction, like the"new"button, anchor as visible defaults, whileplacement: .secondaryActionitems such as"reply"and"flag"are surfaced as candidates the user can promote or hide in the toolbar editor.Build each item's content as the visible control
The closure trailing each ToolbarItem produces the view the customizable slot renders. The example fills its slots with Buttons such as
Button("New", systemImage: "square.and.pencil"), so the identity and placement live on the CustomizableToolbarContent while the actual label and action live on the control inside.
placement: .secondaryAction to .primaryAction, then enter the toolbar's edit mode to watch how customizable items that begin in different placements behave when rearranged.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct CustomizableToolbarContentDemo: View {
var body: some View {
NavigationStack {
Text("Tap and hold the toolbar to customize")
.padding()
.navigationTitle("Inbox")
.toolbar(id: "main") {
ToolbarItem(id: "new", placement: .primaryAction) {
Button("New", systemImage: "square.and.pencil") {}
}
ToolbarItem(id: "reply", placement: .secondaryAction) {
Button("Reply", systemImage: "arrowshape.turn.up.left") {}
}
ToolbarItem(id: "flag", placement: .secondaryAction) {
Button("Flag", systemImage: "flag") {}
}
}
}
}
}