- Published on
Add Spacing to Toolbars with ToolbarSpacer in SwiftUI
- Authors

- Name
- Mick MacCallum
- @0x7fs
iOS 26 introduced ToolbarSpacer, a dedicated API for adding spacing between toolbar items. Before iOS 26, controlling spacing required workarounds using ToolbarItemGroup and regular Spacer() views. Now you have a clean, purpose-built solution with both fixed and flexible spacing options.
Basic Usage
Place ToolbarSpacer between toolbar items to add space:
struct ContentView: View {
var body: some View {
NavigationStack {
Text("Content")
.toolbar {
ToolbarItem {
Button("Delete", systemImage: "trash", role: .destructive) { }
}
ToolbarSpacer(.fixed)
ToolbarItem {
Button("Add", systemImage: "plus") { }
}
}
}
}
}
This creates a fixed amount of space between the Delete and Add buttons.
Fixed vs Flexible Spacing
ToolbarSpacer supports two spacing modes:
Fixed Spacing
Fixed spacing adds a consistent, system-defined gap between items:
.toolbar {
ToolbarItem { Button("First", systemImage: "1.circle") { } }
ToolbarSpacer(.fixed)
ToolbarItem { Button("Second", systemImage: "2.circle") { } }
ToolbarSpacer(.fixed)
ToolbarItem { Button("Third", systemImage: "3.circle") { } }
}
Use fixed spacing for:
- Consistent separation between related items
- Visual grouping
- Predictable layout
Flexible Spacing
Flexible spacing expands to fill available space, pushing items apart:
.toolbar {
ToolbarItem { Button("Left", systemImage: "arrow.left") { } }
ToolbarSpacer(.flexible)
ToolbarItem { Button("Right", systemImage: "arrow.right") { } }
}
Use flexible spacing for:
- Items at opposite ends of the toolbar
- Maximum separation between groups
- Dynamic spacing that adapts to available space
Combining Multiple Spacers
Create sophisticated layouts by combining spacers:
.toolbar {
ToolbarItem { Button("Action 1", systemImage: "1.circle") { } }
ToolbarItem { Button("Action 2", systemImage: "2.circle") { } }
ToolbarSpacer(.flexible)
ToolbarItem { Button("Action 3", systemImage: "3.circle") { } }
ToolbarSpacer(.fixed)
ToolbarItem { Button("Action 4", systemImage: "4.circle") { } }
}
This groups Action 1 and 2 on the left, then uses flexible spacing to push Action 3 and 4 to the right with fixed spacing between them.
Targeting Specific Placements
Specify placement for spacers in specific toolbar regions:
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button("Leading", systemImage: "arrow.left") { }
}
ToolbarSpacer(.fixed, placement: .topBarLeading)
ToolbarItem(placement: .topBarLeading) {
Button("Also Leading", systemImage: "star") { }
}
ToolbarItem(placement: .topBarTrailing) {
Button("Trailing", systemImage: "arrow.right") { }
}
}
Practical Example: Document Editor
Here's a toolbar with multiple action groups:
struct DocumentEditor: View {
var body: some View {
NavigationStack {
TextEditor(text: .constant("Document content"))
.navigationTitle("Document")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel", systemImage: "xmark") { }
}
ToolbarItemGroup(placement: .primaryAction) {
Button("Bold", systemImage: "bold") { }
Button("Italic", systemImage: "italic") { }
Button("Underline", systemImage: "underline") { }
ToolbarSpacer(.fixed)
Button("Link", systemImage: "link") { }
}
}
}
}
}
Bottom Toolbar Example
ToolbarSpacer works in bottom toolbars too:
struct PhotoEditor: View {
var body: some View {
NavigationStack {
Image(systemName: "photo")
.resizable()
.scaledToFit()
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
Button("Rotate", systemImage: "rotate.right") { }
Button("Crop", systemImage: "crop") { }
ToolbarSpacer(.flexible)
Button("Filters", systemImage: "camera.filters") { }
ToolbarSpacer(.flexible)
Button("Adjust", systemImage: "slider.horizontal.3") { }
}
}
}
}
}
Comparing with Pre-iOS 26 Approaches
Before iOS 26, you had to use workarounds:
// Before iOS 26
.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
Button("First") { }
Spacer()
Button("Second") { }
}
}
// iOS 26+
.toolbar {
ToolbarItem { Button("First") { } }
ToolbarSpacer(.flexible)
ToolbarItem { Button("Second") { } }
}
The new approach is cleaner and more explicit.
Best Practices
Use Fixed for Grouping
Visually group related items with fixed spacing:
.toolbar {
// Text formatting group
ToolbarItem { Button("Bold") { } }
ToolbarSpacer(.fixed)
ToolbarItem { Button("Italic") { } }
ToolbarSpacer(.fixed)
ToolbarItem { Button("Underline") { } }
ToolbarSpacer(.flexible)
// Alignment group
ToolbarItem { Button("Left") { } }
ToolbarSpacer(.fixed)
ToolbarItem { Button("Center") { } }
}
Use Flexible for Balance
Create balanced, symmetrical layouts:
.toolbar {
ToolbarItem { Button("Left") { } }
ToolbarSpacer(.flexible)
ToolbarItem { Button("Center") { } }
ToolbarSpacer(.flexible)
ToolbarItem { Button("Right") { } }
}
Consider Touch Targets
Ensure adequate spacing for comfortable tapping:
// Good: Fixed spacing ensures minimum separation
.toolbar {
ToolbarItem { Button("A") { } }
ToolbarSpacer(.fixed)
ToolbarItem { Button("B") { } }
}
Maintain Visual Hierarchy
Use spacing to communicate importance:
.toolbar {
ToolbarItem { Button("Save") { } }
ToolbarSpacer(.fixed)
ToolbarItem { Button("Share") { } }
ToolbarSpacer(.flexible)
ToolbarItem { Button("Delete", role: .destructive) { } }
}
Working with ToolbarItemGroup
ToolbarSpacer works seamlessly with ToolbarItemGroup:
.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
Button("Edit", systemImage: "pencil") { }
Button("Copy", systemImage: "doc.on.doc") { }
ToolbarSpacer(.fixed)
Button("Share", systemImage: "square.and.arrow.up") { }
ToolbarSpacer(.flexible)
Button("More", systemImage: "ellipsis") { }
}
}
Continue Learning
Building a Floating Action Button (FAB) that Respects Keyboard in SwiftUI
Learn how to create a Material Design-style floating action button in SwiftUI that intelligently moves above the keyboard and avoids blocking text input fields.
Aligning Text in SwiftUI
Learn how to align single and multiline text in SwiftUI.
How to scale text to fit its parent view with SwiftUI
Learn the ways to scale a text view's font size to fit its parent view in SwiftUI.
