SwiftUI.cc
Guides
GuideNavigationBeginner

How to Navigate Between Views in SwiftUI

Learn how to navigate between views in SwiftUI using NavigationLink and NavigationStack.

5 min readUpdated 2026-06

Overview

Navigating between views is a fundamental part of building multi-screen apps in SwiftUI. You can achieve this using the NavigationLink and NavigationStack APIs, which provide a declarative and intuitive way to manage view transitions.

The Approach

Here's how to navigate between two views using NavigationLink and NavigationStack:

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationStack {
            VStack {
                Text("Home Screen")
                NavigationLink("Go to Details", destination: DetailsView())
            }
            .padding()
        }
    }
}

struct DetailsView: View {
    var body: some View {
        Text("Details Screen")
            .navigationTitle("Details")
    }
}

Step-by-Step Breakdown

  1. NavigationStack: Wraps your content and manages the navigation stack. This replaces NavigationView in iOS 16+.
  2. NavigationLink: When tapped, it pushes the destination view (in this case DetailsView) onto the navigation stack.
  3. DetailsView: A simple view that appears after navigation. The .navigationTitle(_:) modifier sets the title in the navigation bar.

This implementation creates a basic navigation flow where the user can tap a link to move from the home screen to a details screen.

Common Variations

Using Programmatic Navigation with @Environment and NavigationPath

You can also navigate programmatically by using the @Environment property to access the NavigationPath:

struct ContentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            VStack {
                Text("Home Screen")
                Button("Go to Details") {
                    path.append("details")
                }
            }
            .padding()
            .navigationDestination(for: String.self) { value in
                if value == "details" {
                    DetailsView()
                }
            }
        }
    }
}

This variation allows you to control navigation from code, which is useful for scenarios like showing a view after an async operation (e.g., login success).

For Earlier iOS Versions (Pre-iOS 16)

For iOS 15 and below, you would use NavigationView instead of NavigationStack:

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack {
                Text("Home Screen")
                NavigationLink("Go to Details", destination: DetailsView())
            }
            .padding()
        }
    }
}

Pitfalls to Avoid

  1. Missing NavigationStack: If you forget to wrap your view hierarchy in NavigationStack, NavigationLink won't work as expected — it will not push the view.
  2. Incorrect NavigationTitle Placement: If you place .navigationTitle(_:) on a container view (like VStack) instead of the destination view itself, the title may not appear correctly.
  3. Unintended Multiple NavigationStacks: Nesting multiple NavigationStacks can cause unexpected behavior like duplicated navigation bars or broken back navigation. Only one NavigationStack should wrap your screen-level navigation.
  • how-to-pass-data-between-views
  • how-to-customize-navigation-bar
  • how-to-present-modal-sheets