Organizing SwiftUI Views with ToolbarContent and @ToolbarContentBuilder


Greetings, traveler!

As SwiftUI projects grow, one of the most common challenges developers face is managing increasingly complex view hierarchies. Even a simple screen can quickly expand into dozens of nested modifiers and view declarations. Since Apple lifted the historical limit of ten nested views, it’s become easier than ever to write deeply nested body implementations—but that also means it’s easier to end up with unreadable and hard-to-maintain code.

Breaking Down Large body Implementations

When your body starts stretching beyond a few dozen lines, readability and maintainability suffer. A proven best practice is to split large SwiftUI views into smaller subviews or extract reusable parts into computed properties or helper functions. This keeps your main layout concise and makes each section easier to understand, test, and reuse.

For example, sections of a form, list items, or card components can easily be extracted into private subviews. The same principle applies to non-visual elements like toolbars — a part of the interface that can easily become bloated when multiple buttons and menu items are introduced.

The Problem with Toolbars

In modern SwiftUI apps, the .toolbar modifier is incredibly powerful, allowing you to define buttons, menus, and controls that adapt to different platforms and navigation contexts. However, when you have several toolbar items — especially if each one has its own action or dynamic label — the code inside .toolbar { ... } can quickly become cluttered.

You might think of moving that logic to a computed property returning some View, but .toolbar does not accept a View type — it specifically expects toolbar content, which follows a different protocol hierarchy.

Meet ToolbarContent and @ToolbarContentBuilder

SwiftUI provides a clean way to handle this with two key constructs:

  • ToolbarContent — a protocol whose conforming types represent items that can be placed in various locations within a toolbar.
  • @ToolbarContentBuilder — a result builder that constructs a set of toolbar items from multiple expressions inside a closure.

By combining these, you can encapsulate your toolbar layout and logic in a dedicated property or function, keeping your body definition minimal and readable.

Example

Here’s a simple example that demonstrates how you can organize toolbar items using ToolbarContent:

struct DemoView: View {
    @State private var message: String = "Hello, world!"

    var body: some View {
        NavigationStack {
            Text(message)
                .font(.title2)
                .padding()
                .navigationTitle("Home")
                .toolbar {
                    toolbarContent
                }
        }
    }

    @ToolbarContentBuilder
    private var toolbarContent: some ToolbarContent {
        ToolbarItem(placement: .navigationBarLeading) {
            Button {
                message = "Left button tapped"
            } label: {
                Label("Left", systemImage: "line.3.horizontal")
            }
        }

        ToolbarItem(placement: .navigationBarTrailing) {
            Button {
                message = "Right button tapped"
            } label: {
                Label("Right", systemImage: "star")
            }
        }
    }
}

Conclusion

Using @ToolbarContentBuilder to separate toolbar content from the main view body has several advantages:

  • Improved readability: Your body remains focused on layout, while toolbar logic lives elsewhere.
  • Better organization: Grouping toolbar items in a dedicated block makes it easier to see their structure and placement at a glance.
  • Scalability: When your toolbar grows to include multiple buttons, menus, or conditional logic, maintaining it becomes much easier.