How to use Toolbar in SwiftUI | SwiftUI Bootcamp #63
The navigation bar is prime real estate — it's the first thing users look at to understand where they are and what they can do. The .toolbar modifier gives you full, declarative control over every item in the bar, including placement, and even lets you add a dropdown navigation menu directly to the title.
What You'll Learn
- How to use
.toolbarwithToolbarItemto add items to specific bar positions - How
ToolbarItemPlacementvalues like.navigationBarLeadingand.navigationBarTrailingwork - How to add a
toolbarTitleMenufor contextual navigation directly in the title area
Mental Model
The navigation bar is like a command bar in a cockpit. There are fixed slots: the left side (leading) and the right side (trailing), plus the center where the title sits. ToolbarItem(placement:) lets you place controls in specific slots. You don't position items with coordinates — you declare which slot each item belongs to and SwiftUI handles the layout for you across all screen sizes and orientations.
The toolbarTitleMenu is like making the cockpit title itself a dropdown — tap the title and a menu appears with additional destinations. This is great for screens where the user might want to jump to a related section quickly.
Detailed Explanation
The .toolbar modifier replaced the deprecated .navigationBarItems(leading:trailing:) modifier. The advantage is flexibility: each ToolbarItem is declared independently with a placement value, and you can have multiple items in the same placement by adding multiple ToolbarItem views inside the .toolbar closure.
Common ToolbarItemPlacement options: .navigationBarLeading (left side of the navigation bar), .navigationBarTrailing (right side), .principal (center, overrides the title), .bottomBar (the toolbar at the bottom of the screen), and .keyboard (above the keyboard). The system positions and sizes items appropriately for each placement.
toolbarTitleMenu is a modifier that adds a chevron indicator to the navigation title and presents a menu when it is tapped. The menu can contain Button views and NavigationLink values, making it useful for switching between major sections of an app without going back to a root screen.
The commented-out modifiers show the evolution of toolbar APIs: .navigationBarHidden(true) was the old way to hide the bar; the new way is .toolbar(.hidden, for: .navigationBar). Similarly, .toolbarBackground(.hidden) hides the bar's background, and .toolbarColorScheme(.dark) forces dark appearance on the bar independent of the app's theme.
Code Structure
ToolbarBootcamp.swift embeds a scrollable list inside a NavigationStack to show the toolbar in context. A leading heart icon and trailing gear icon demonstrate dual toolbar items. The toolbarTitleMenu shows how to build a navigation dropdown from the title. The commented-out code preserves the deprecated API patterns for reference.
Complete Code
ToolbarBootcamp.swift
import SwiftUI
struct ToolbarBootcamp: View {
@State private var text: String = ""
@State private var paths: [String] = [] // Navigation path for programmatic navigation
var body: some View {
NavigationStack(path: $paths) {
ZStack {
Color.white.ignoresSafeArea()
ScrollView {
TextField("Placeholder", text: $text)
ForEach(0..<50) { _ in
Rectangle()
.fill(Color.blue)
.frame(width: 200, height: 200)
}
}
}
.navigationTitle("Toolbar")
// .navigationBarItems(
// leading: Image(systemName: "heart.fill"),
// trailing: Image(systemName: "gear")
// )
.toolbar {
ToolbarItem(placement: .navigationBarLeading) { // Pins to left side of nav bar
Image(systemName: "heart.fill")
}
ToolbarItem(placement: .navigationBarTrailing) { // Pins to right side of nav bar
// HStack {
// Image(systemName: "house.fill")
Image(systemName: "gear")
// }
}
}
// .navigationBarHidden(true)
// .toolbar(.hidden, for: .navigationBar) // Modern way to hide the toolbar
// .toolbarBackground(.hidden, for: .navigationBar) // Hide toolbar background only
// .toolbarColorScheme(.dark, for: .navigationBar) // Force dark appearance on toolbar
.navigationBarTitleDisplayMode(.inline) // Keeps title compact, enabling title menu tap area
.toolbarTitleMenu { // Adds a dropdown chevron to the navigation title
Button("Screen 1") {
paths.append("Screen 1") // Push Screen 1 via programmatic navigation
}
Button("Screen 2") {
paths.append("Screen 2") // Push Screen 2 via programmatic navigation
}
}
.navigationDestination(for: String.self) { value in
Text("New screen: \(value)")
}
}
}
}
struct ToolbarBootcamp_Previews: PreviewProvider {
static var previews: some View {
ToolbarBootcamp()
}
}Code Walkthrough
@State private var paths: [String] = []— This array backs theNavigationStackpath. ThetoolbarTitleMenubuttons append to it, demonstrating that toolbar actions can trigger navigation just like any other programmatic navigation..toolbar { ... }— The closure acceptsToolbarItemviews. This is more flexible than the oldnavigationBarItemsbecause you can have multiple items per side and you can conditionally include items usingifstatements inside the closure.ToolbarItem(placement: .navigationBarLeading)— Declares an item for the left slot. The system respects system buttons (like a back button) and places your item alongside them. On iPad, leading toolbar items may appear in different positions depending on the split view layout..navigationBarTitleDisplayMode(.inline)— Displays the title inline (small, in the center) rather than the large title style. ThetoolbarTitleMenurequires.inlinemode to work correctly — the dropdown chevron only appears in inline mode..toolbarTitleMenu— The contents of this closure appear in a dropdown when the user taps the title. Buttons inside it can do anything — navigate, change state, show sheets. Here they push new screens onto the navigation path..navigationDestination(for: String.self)— Resolves anyStringvalue pushed ontopathsto aTextview. This is the same pattern from theNavigationStacklesson — toolbar navigation and link navigation both use the same destination registration.
Common Mistakes
Mistake: Using the deprecated .navigationBarItems(leading:trailing:)
This API still compiles but is deprecated since iOS 16. It doesn't support multiple items per side and is not composable. Migrate to .toolbar { ToolbarItem(...) } for all new code.
Mistake: Adding multiple items to the same placement using an HStack
Wrapping two icons in an HStack inside a single ToolbarItem works but gives you no semantic separation between the items. Prefer individual ToolbarItem views per control — this also makes it easier to conditionally show or hide specific items.
Mistake: Expecting toolbarTitleMenu to work with large title display mode
The dropdown chevron only appears when .navigationBarTitleDisplayMode(.inline) is set. With .large or .automatic, the title dropdown is suppressed. Always test your toolbar in the device simulator to catch display mode issues.
Key Takeaways
- Use
.toolbar { ToolbarItem(placement:) }instead of the deprecated.navigationBarItems— it's composable and placement-aware toolbarTitleMenuturns the navigation title into a navigable dropdown, useful for switching contexts without going back to root- The navigation toolbar and the bottom toolbar are independent — you can populate both using different
ToolbarItemPlacementvalues in a single.toolbarmodifier
Last updated: June 27, 2026