The navigation bar serves as the primary control interface at the top of the editor, housing essential functions like session management (close/save), editing operations (undo/redo), mode switching, and export capabilities. This guide shows you how to customize the navigation layout, button placement, and functionality to align with your app’s information architecture and user flow patterns. While examples use the Design Editor, the same configuration principles apply to all editor solutions.
Explore a complete code sample on GitHub.
Navigation Bar Architecture#

The navigation bar displays horizontally at the top of the editor, organized into three placement areas: leading (left), principal (center), and trailing (right).
Key Components:
NavigationBar.Item- Protocol that all navigation bar items conform toNavigationBar.Button- Pre-built button implementation with action and labelNavigationBar.ItemGroup- Container that groups items by placement (leading, principal, trailing)NavigationBar.Context- Provides access to the engine, editor state, and event handler- Custom Items - Create fully custom components by implementing
NavigationBar.Item
Configuration#
Navigation bar customization uses SwiftUI modifiers in the .imgly namespace. Items are organized into placement groups similar to SwiftUI’s ToolbarItemGroup.
Available modifiers:
-
navigationBarItems- Define the complete list of navigation bar items grouped by placement. Items are only displayed whenisVisible(_:)returnstrue. -
modifyNavigationBarItems- Modify the default item list by adding, replacing, or removing specific items without rebuilding the entire configuration.
The NavigationBar.Context provides access to the engine, editor state, asset library, and event handler for advanced customization logic.
Default Navigation Bar Items#
Each editor solution has its own default navigation bar configuration optimized for its workflow:
Design Editor:
builder.navigationBar { navigationBar in navigationBar.items { _ in NavigationBar.ItemGroup(placement: .topBarLeading) { NavigationBar.Buttons.closeEditor() } NavigationBar.ItemGroup(placement: .topBarTrailing) { NavigationBar.Buttons.undo() NavigationBar.Buttons.redo() NavigationBar.Buttons.togglePagesMode() NavigationBar.Buttons.export() } }}Photo Editor:
builder.navigationBar { navigationBar in navigationBar.items { _ in NavigationBar.ItemGroup(placement: .topBarLeading) { NavigationBar.Buttons.closeEditor() } NavigationBar.ItemGroup(placement: .topBarTrailing) { NavigationBar.Buttons.undo() NavigationBar.Buttons.redo() NavigationBar.Buttons.togglePreviewMode() NavigationBar.Buttons.export() } }}Video Editor:
builder.navigationBar { navigationBar in navigationBar.items { _ in NavigationBar.ItemGroup(placement: .topBarLeading) { NavigationBar.Buttons.closeEditor() } NavigationBar.ItemGroup(placement: .topBarTrailing) { NavigationBar.Buttons.undo() NavigationBar.Buttons.redo() NavigationBar.Buttons.export() } }}Apparel Editor:
builder.navigationBar { navigationBar in navigationBar.items { _ in NavigationBar.ItemGroup(placement: .topBarLeading) { NavigationBar.Buttons.closeEditor() } NavigationBar.ItemGroup(placement: .principal) { NavigationBar.Buttons.undo() NavigationBar.Buttons.redo() NavigationBar.Buttons.togglePreviewMode() } NavigationBar.ItemGroup(placement: .topBarTrailing) { NavigationBar.Buttons.export() } }}Postcard Editor:
builder.navigationBar { navigationBar in navigationBar.items { _ in NavigationBar.ItemGroup(placement: .topBarLeading) { NavigationBar.Buttons.closeEditor() NavigationBar.Buttons.previousPage( label: { _ in NavigationLabel("Design", direction: .backward) }, ) } NavigationBar.ItemGroup(placement: .principal) { NavigationBar.Buttons.undo() NavigationBar.Buttons.redo() NavigationBar.Buttons.togglePreviewMode() } NavigationBar.ItemGroup(placement: .topBarTrailing) { NavigationBar.Buttons.nextPage( label: { _ in NavigationLabel("Write", direction: .forward) }, ) NavigationBar.Buttons.export() } }}Modify Navigation Bar Items#
Use navigationBar.modify to adjust the default item list without rebuilding the entire configuration:
navigationBar.modify { _, items inParameters:
context- provides access to the engine, editor state, and event handleritems- mutable array of navigation bar item groups that can be modified
Available modification operations:
addFirst- prepends new items at the beginning of a placement group:
items.addFirst(placement: .topBarTrailing) { NavigationBar.Button(id: "my.package.inspectorBar.button.first") { _ in print("First Button in top bar trailing placement group action") } label: { _ in Label("First Button", systemImage: "arrow.backward.circle") }}addLast- appends new items at the end of a placement group:
items.addLast(placement: .topBarLeading) { NavigationBar.Button(id: "my.package.inspectorBar.button.last") { _ in print("Last Button in top bar leading placement group action") } label: { _ in Label("Last Button", systemImage: "arrow.forward.circle") }}addAfter- adds new items right after a specific item:
items.addAfter(id: NavigationBar.Buttons.ID.undo) { NavigationBar.Button(id: "my.package.inspectorBar.button.afterUndo") { _ in print("After Undo") } label: { _ in Label("After Undo", systemImage: "arrow.forward.square") }}addBefore- adds new items right before a specific item:
items.addBefore(id: NavigationBar.Buttons.ID.redo) { NavigationBar.Button(id: "my.package.inspectorBar.button.beforeRedo") { _ in print("Before Redo") } label: { _ in Label("Before Redo", systemImage: "arrow.backward.square") }}replace- replaces an existing item with new items:
items.replace(id: NavigationBar.Buttons.ID.closeEditor) { NavigationBar.Buttons.closeEditor( label: { _ in Label("Cancel", systemImage: "xmark") }, )}items.replace(id: NavigationBar.Buttons.ID.export) { NavigationBar.Buttons.export( label: { _ in Label("Done", systemImage: "checkmark") }, )}remove- removes an existing item:
items.remove(id: NavigationBar.Buttons.ID.togglePagesMode)NavigationBar.Item Configuration#
Each NavigationBar.Item requires a unique id for SwiftUI’s ForEach rendering. You have multiple options for creating navigation bar items, from simple predefined buttons to fully custom implementations. Items must be organized within NavigationBar.ItemGroup containers.
Use Predefined Buttons#
Start with predefined buttons from the NavigationBar.Buttons namespace. All available predefined buttons are listed below.
NavigationBar.Buttons.closeEditor()Customize Predefined Buttons#
Customize any predefined button by overriding its default parameters:
NavigationBar.Buttons.undo( action: { context in try context.engine?.editor.undo() }, label: { context in Label { Text("Undo") } icon: { Image.imgly.undo } .opacity(context.state.viewMode == .preview ? 0 : 1) .labelStyle(.imgly.adaptiveIconOnly) }, isEnabled: { context in try !context.state.isCreating && context.state.viewMode != .preview && context.engine?.editor.canUndo() == true }, isVisible: { _ in true },)Available parameters:
-
action- the action to perform when the user triggers the button. Uses the engine to perform an undo step in this example. -
label- the view that describes the purpose of the button’s action. Shows conditional opacity based on view mode in this example. -
isEnabled- whether the button is enabled. This example checks if undo is available and editor state. -
isVisible- whether the button should be visible. Can reserve layout space when hidden using the label view instead of this parameter.
Create New Buttons#
Create custom buttons when predefined options don’t meet your needs:
NavigationBar.Button( id: "my.package.navigationBar.button.newButton",) { _ in print("New Button action")} label: { _ in Label("New Button", systemImage: "star.circle")} isEnabled: { _ in true} isVisible: { _ in true}Required and optional parameters:
-
id- the unique id of the button. This parameter is required. -
action- the action to perform when the user triggers the button. This parameter is required. -
label- aViewthat describes the purpose of the button’s action. Don’t encode visibility logic in this view. This parameter is required. -
isEnabled- whether the button is enabled. By default, true is always used. -
isVisible- whether the button should be visible. Can reserve layout space when hidden using the label view instead of this parameter. By default, true is always used.
Create New Custom Items#
For completely custom implementations, create a type conforming to the NavigationBar.Item protocol:
private struct CustomNavigationBarItem: NavigationBar.Item { var id: EditorComponentID { "my.package.navigationBar.newCustomItem" }
func body(_ context: NavigationBar.Context) throws -> some View { ZStack { RoundedRectangle(cornerRadius: 10) .fill(.conicGradient(colors: [.red, .yellow, .green, .cyan, .blue, .purple, .red], center: .center)) Text("New Custom Item") .padding(4) } .onTapGesture { print("New Custom Item action") } }
func isVisible(_ context: NavigationBar.Context) throws -> Bool { true }}Then use it in your navigation bar items:
CustomNavigationBarItem()Protocol requirements:
-
var id: EditorComponentID { get }- the unique id of the item. This property is required. -
func body(_: NavigationBar.Context) throws -> some View- the body of your view. Don’t encode visibility logic in this view unless layout space should be reserved when hidden. This property is required. -
func isVisible(_: NavigationBar.Context) throws -> Bool- whether the item should be visible. Prefer using this parameter for visibility logic unless layout space should be reserved when hidden. By default, true is always used.
List of Available NavigationBar.Buttons#
All predefined buttons are available as static functions in the NavigationBar.Buttons namespace. Each function returns a NavigationBar.Button with default parameters that you can customize as shown in the Customize Predefined Buttons section.
| Button | ID | Description |
|---|---|---|
NavigationBar.Buttons.closeEditor | NavigationBar.Buttons.ID.closeEditor | Closes editor via editor event .closeEditor. |
NavigationBar.Buttons.undo | NavigationBar.Buttons.ID.undo | Does undo operation in the editor via EditorAPI.undo engine API. |
NavigationBar.Buttons.redo | NavigationBar.Buttons.ID.redo | Does redo operation in the editor via EditorAPI.redo engine API. |
NavigationBar.Buttons.export | NavigationBar.Buttons.ID.export | Triggers onExport callback via editor event .startExport. |
NavigationBar.Buttons.togglePreviewMode | NavigationBar.Buttons.ID.togglePreviewMode | Updates editor view mode via editor event .setViewMode: when current view mode is EditorViewMode.edit, then EditorViewMode.preview is set and vice versa. Note that this button is intended to be used in Photo Editor, Apparel Editor and Postcard Editor and may cause unexpected behaviors when used in other solutions. |
NavigationBar.Buttons.togglePagesMode | NavigationBar.Buttons.ID.togglePagesMode | Updates editor view mode via editor event .setViewMode: when current view mode is EditorViewMode.edit, then EditorViewMode.pages is set and vice versa. Note that this button is intended to be used in Design Editor and may cause unexpected behaviors when used in other solutions. |
NavigationBar.Buttons.previousPage | NavigationBar.Buttons.ID.previousPage | Navigates to the previous page via editor event .navigateToPreviousPage. |
NavigationBar.Buttons.nextPage | NavigationBar.Buttons.ID.nextPage | Navigates to the next page via editor event .navigateToNextPage. |