How to Navigate Between Views in SwiftUI
Learn how to navigate between views in SwiftUI using NavigationLink and NavigationStack.
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
- NavigationStack: Wraps your content and manages the navigation stack. This replaces
NavigationViewin iOS 16+. - NavigationLink: When tapped, it pushes the destination view (in this case
DetailsView) onto the navigation stack. - 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
- Missing NavigationStack: If you forget to wrap your view hierarchy in
NavigationStack,NavigationLinkwon't work as expected — it will not push the view. - Incorrect NavigationTitle Placement: If you place
.navigationTitle(_:)on a container view (likeVStack) instead of the destination view itself, the title may not appear correctly. - Unintended Multiple NavigationStacks: Nesting multiple
NavigationStacks can cause unexpected behavior like duplicated navigation bars or broken back navigation. Only oneNavigationStackshould wrap your screen-level navigation.
Related Guides
- how-to-pass-data-between-views
- how-to-customize-navigation-bar
- how-to-present-modal-sheets