← Back to list

NavigationStack and NavigationPath in SwiftUI

1️⃣ What is NavigationStack?

Niraj Paul · 2025-12-16 04:31 · 2 claps · 2.3 min read paywalled
#navigationpath #navigation #swiftui #swift
Open on Medium ↗
Wiki topics: 📱 · Mobile Development

NavigationStack and NavigationPath in SwiftUI

1️⃣ What is NavigationStack?

👉 Definition

NavigationStack is the modern SwiftUI navigation container (iOS 16+). It replaces NavigationView and manages a stack-based navigation flow.

📦 Think of it like a stack of screens:

  • Push → add a screen on top
  • Pop → remove the top screen

✅ Basic Example

NavigationStack {
    Text("Home Screen")
        .navigationTitle("Home")
}

2️⃣ What is NavigationPath?

👉 Definition

NavigationPath is a data model that represents the entire navigation history of a NavigationStack.

Instead of navigation being driven by views, it’s driven by data.

🧠 Mental Model

NavigationPath = [Screen1, Screen2, Screen3]

If you modify the array → navigation updates automatically.

✅ Simple Example

@State private var path = NavigationPath()

NavigationStack(path: $path) {
    Button("Go to Details") {
        path.append("details")
    }
}

3️⃣ Navigation without NavigationPath (Simple case)

Use this when navigation is static and linear.

NavigationStack {
    NavigationLink("Go to Details", value: "details")
        .navigationDestination(for: String.self) { value in
            Text("Details Screen")
        }
}

✔ Good for:

  • Simple apps
  • Few screens
  • No deep linking

4️⃣ Navigation WITH NavigationPath (Recommended)

Use this when navigation is dynamic, programmatic, or deep-linked.

🧩 Full Example (Best Practice)

enum Screen: Hashable {
    case home
    case details
    case profile
}

struct ContentView: View {
    @State private var path = NavigationPath()
    var body: some View {
        NavigationStack(path: $path) {
            VStack(spacing: 20) {
                Button("Go to Details") {
                    path.append(Screen.details)
                }
                Button("Go to Profile") {
                    path.append(Screen.profile)
                }
            }
            .navigationTitle("Home")
            .navigationDestination(for: Screen.self) { screen in
                switch screen {
                case .details:
                    Text("Details Screen")
                case .profile:
                    Text("Profile Screen")
                case .home:
                    Text("Home Screen")
                }
            }
        }
    }
}

📌 Final Cheat Sheet

NavigationStack          // Container
NavigationPath           // State
navigationDestination    // Screen resolver
path.append()            // Push
path.removeLast()        // Pop
path = NavigationPath()  // Reset

Example: NavigationPath in Action (Details Explanation)

Step 1: Enum for Routes

Define multiple destinations for the navigation stack with an enum. Each case can carry different types of associated data.

// Define your screens
enum Route: Hashable {  
    case view1.          // No associated data
    case view2(id: Int) // Int type
    case view3(String)  // Associated value for data passing
}

Step 2: Use NavigationPath

The NavigationPath will type-erase the list of Route values you push into it. Create router Class (ObservableObject): This class manages the navigation logic:

import SwiftUI
class Router: ObservableObject {
    @Published var path = NavigationPath()
    func pushView(_ route: Route) {
        path.append(route)
    }
    func popToRoot() {
        path = NavigationPath()  // Clears the stack
    }
    func popToSpecificView(count: Int) {
        path.removeLast(count)  // Removes `count` views from the stack
    }
}

Step 2: Create Views

Each view interacts with the Router object.

// View 1
// Define your screens
struct View1: View {
    @EnvironmentObject var router: Router
    var body: some View {
        VStack {
            Text("View 1")
            Button("Go to View 2") {
                router.pushView(.view2)
            }
        }
    }
}
//***************************************************
// View 2
struct View2: View {
    @EnvironmentObject var router: Router
    var body: some View {
        VStack {
            Text("View 2")
            Button("Go to View 3") {
                router.pushView(.view3("Niraj Paul"))  // Passing data
            }
        }
    }
}
//***************************************************
// View 3
struct View3: View {
    @EnvironmentObject var router: Router
    var name: String
    var body: some View {
        VStack {
            Text("Hello, \(name)")
            Button("Go to Root") {
                router.popToRoot()  // Pop to root view
            }
            Button("Go to View 1") {
                router.popToSpecificView(count: 2)  // Go back two views
            }
        }
    }
}

Step 3: NavigationStack Container

This container links the router to the views and manages navigation.

struct AppContainerView: View {
    @StateObject var router = Router()
    var body: some View {
        NavigationStack(path: $router.path) {
            View1()
                .navigationDestination(for: Route.self) { route in
                    switch route {
                    case .view1:
                        View1()
                    case .view2:
                        View2()
                    case .view3(let name):
                        View3(name: name)
                    }
                }
        }
        .environmentObject(router)  // Provide Router to all views
    }
}

Thank you

Niraj Paul


메타데이터
post_id
33a5c7a51eaa
slug
navigationstack-and-navigationpath-in-swiftui-33a5c7a51eaa
url
https://medium.com/@nirajpaul2/navigationstack-and-navigationpath-in-swiftui-33a5c7a51eaa
canonical_url
https://medium.com/@nirajpaul2/navigationstack-and-navigationpath-in-swiftui-33a5c7a51eaa
author_url
https://medium.com/@nirajpaul2
status
ok
fetched_at
2026-08-27 20:45:58