NavigationLink — what is it, transition button in SwiftUI

Author: IT Sectr Published: 2026-06-25 Reading time: 6 min

NavigationLink is a control element in SwiftUI designed for transitioning to another screen in NavigationStack or NavigationView. According to Apple Developer Documentation, 2024, NavigationLink creates a button that, when pressed, places the target View onto the navigation stack. In iOS 16+ it is recommended to use NavigationLink with value and NavigationDestination rather than with destination directly to avoid premature initialization of target Views.

Key Takeaways

  • NavigationLink — a button for navigating to another screen in SwiftUI
  • Two forms — with destination:label: and with value:label:
  • Value form recommended in iOS 16+ (NavigationStack)
  • Destination form causes premature View initialization
  • Automatic disclosure arrow in List

NavigationLink is a View that initiates a navigation transition when pressed. Inside NavigationStack, pressing NavigationLink places the target screen onto the stack and displays the system Back button. NavigationLink has existed since iOS 13 and is the primary method of user navigation in SwiftUI.

NavigationLink does not inherit from UIButton — it is a SwiftUI View that automatically adapts to context. Inside List, NavigationLink displays with a disclosure indicator. Outside a list, NavigationLink behaves like a regular button but with navigation behavior.

According to SwiftUI Lab (2024), NavigationLink is one of the most used Views in SwiftUI applications, second only to Text, Image, and VStack. Understanding the differences between initialization forms is critical for performance and predictable navigation behavior.

How NavigationLink works under the hood

When pressed, NavigationLink adds a value (or destination) to the navigation stack associated with the nearest NavigationStack or NavigationView. SwiftUI uses EnvironmentValue to pass the navigation path through the View hierarchy. NavigationLink reads this path from the Environment and modifies it when pressed.

NavigationLink has two main forms: with destination (directly specifying the target View) and with value (value for NavigationDestination). The choice of form depends on the iOS version and navigation architecture.

FormInitializeriOS 13–15iOS 16+
DestinationNavigationLink(destination:label:)RecommendedNot recommended
ValueNavigationLink(value:label:)Not availableRecommended
IsActiveNavigationLink(isActive:destination:label:)Programmatic navigationNot recommended

Destination form (iOS 13+): NavigationLink(destination: DetailView(), label: { Text("Open") }). This form creates DetailView immediately when rendering NavigationLink, even if the user has not clicked the link. This leads to premature View initialization and potential performance issues if the destination View performs heavy operations in its initializer.

Value form (iOS 16+): NavigationLink(value: "detail_42", label: { Text("Open") }). The target View is created only when the link is pressed, when SwiftUI finds the corresponding .navigationDestination. This prevents premature initialization and makes navigation more predictable.

NavigationLink with NavigationStack in iOS 16+ requires switching to the value form. You define a data type for navigation (String, Int, enum Route) and register the destination via .navigationDestination. NavigationLink only places the value onto the stack, and SwiftUI creates the target View when pressed.

swift
struct CatalogView: View {
    let categories: [String]

    var body: some View {
        List(categories, id: \.self) { category in
            NavigationLink(value: category) {
                Text(category)
            }
        }
        .navigationDestination(for: String.self) { category in
            CategoryView(name: category)
        }
    }
}

// Programmatic navigation:
struct DeepLinkView: View {
    @State private var path: [AppRoute] = []

    var body: some View {
        NavigationStack(path: $path) {
            HomeView()
                .navigationDestination(for: AppRoute.self) { route in
                    switch route {
                    case .detail(let id): DetailView(id: id)
                    case .settings: SettingsView()
                    }
                }
                .toolbar {
                    Button("Open Settings") {
                        path.append(AppRoute.settings)
                    }
                }
        }
    }
}

Programmatic navigation: adding a value to the path (via path.append) is equivalent to pressing a NavigationLink with the same value. This allows implementing navigation from ViewModel, Coordinator, or in response to push notifications.

IsActive form (NavigationLink(isActive:destination:label:)) is available for compatibility but not recommended in iOS 16+. Use the value form with Binding to a path array or NavigationPath.

NavigationLink in List automatically displays a disclosure indicator (chevron) on the right side of the row, signaling to the user that pressing will lead to another screen. List manages the arrow display automatically — unlike a regular NavigationLink outside a list, where there is no arrow.

With iOS 16, List with NavigationLink automatically uses the value form inside List(data:rowContent:). When using ForEach inside List, the disclosure indicator is also added automatically. This behavior cannot be disabled through modifiers — only replacing NavigationLink with Button can remove the arrow.

Problem with destination form in List: if you use NavigationLink(destination:label:) inside List, all destination Views are created immediately when loading the list, regardless of whether the user clicked the link or not. For lists with a large number of rows, this can significantly slow down initial loading and increase memory consumption. The value form with NavigationStack solves this problem.

According to WWDC 2022 (Session 10054), Apple recommends using NavigationStack and the value form of NavigationLink for new projects. This is especially important for List with dynamic data, where the number of rows can be large.

Pattern 1: Custom NavigationLink appearance. NavigationLink accepts any View as a label, allowing you to create custom designs for the link. Inside List, this is especially convenient — you get an automatic disclosure indicator when using NavigationLink.

swift
NavigationLink(value: ProductRoute.detail(product)) {
    HStack {
        AsyncImage(url: product.imageURL)
            .frame(width: 60, height: 60)
        VStack(alignment: .leading) {
            Text(product.name).font(.headline)
            Text(product.price) .foregroundColor(.secondary)
        }
    }
    .padding(8)
}

Pattern 2: NavigationLink without arrow (custom button). If you do not need a disclosure indicator, use Button for programmatic navigation: path.append(value). This is useful for custom interface elements where NavigationLink looks unnatural.

Pattern 3: Conditional navigation. You can block NavigationLink by using an empty destination or not adding .navigationDestination for certain values. Programmatic navigation via path allows checking conditions before adding a value.

According to Hacking with Swift (2024), most problems with NavigationLink are related to using the destination form in older projects. When migrating to NavigationStack, replace all NavigationLink(destination:label:) with NavigationLink(value:label:) and add .navigationDestination at the root level.

Frequently Asked Questions

What is NavigationLink in SwiftUI?

NavigationLink is a View for transitioning to another screen in SwiftUI. When pressed, it places the target screen into the NavigationStack or NavigationView navigation stack. It supports two forms: with destination (target View) and with value (routing value).

Which NavigationLink form is better: destination or value?

Value form (iOS 16+) is preferable: the target View is created only when pressed, not when rendering the link. The destination form creates the View immediately, which can cause performance issues. For iOS 16+ projects, use value + NavigationDestination.

Why does NavigationLink create an arrow in List?

SwiftUI automatically adds a disclosure indicator (arrow) to NavigationLink inside List, signaling the possibility of navigation. This behavior cannot be disabled. If the arrow is not needed, use Button with programmatic navigation via path.append().

How to do programmatic navigation through NavigationLink?

Use NavigationStack with a Binding path and add values via path.append(value). This is equivalent to pressing a NavigationLink with the same value. Programmatic navigation allows implementing deeplinks, push notifications, and the Coordinator pattern.

Does NavigationLink affect performance?

The destination form can affect performance if target Views perform heavy operations in their initializer — all destinations are created when the list is rendered. The value form with NavigationStack solves this problem by creating Views only when pressed. For lists with 50+ rows, the difference is significant.

Summary

  • NavigationLink — a button for navigation transitions between SwiftUI screens
  • Value form recommended in iOS 16+ with NavigationStack
  • Destination form creates View prematurely — avoid for large lists
  • Disclosure indicator — automatic arrow in List (cannot be disabled)
  • Programmatic navigation via path.append() for deeplinks and Coordinator
  • NavigationDestination registers target screens by data types
  • IsActive form — deprecated, use value form on iOS 16+

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also