NavigationLink Works Only for Once
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
A common SwiftUI bug causes NavigationLink to work only once — you tap it, navigate to the detail view, go back, and the same link stops responding. This happens because SwiftUI's state management marks the link as "already activated" and never resets it. The fix depends on which NavigationLink API you use: the older isActive binding, the programmatic NavigationStack with navigationDestination, or the implicit push style. In most cases, switching to NavigationStack (iOS 16+) or ensuring proper state reset resolves the issue.
The Problem
On iPad or with NavigationView in a split-view context, the link activates once, pushes the detail, but after dismissal it becomes unresponsive. This is a known SwiftUI behavior related to how NavigationView manages its column-based navigation internally.
Fix 1: Use NavigationStack (iOS 16+)
NavigationStack replaced NavigationView in iOS 16 and does not suffer from the one-time activation bug. It uses a value-based navigation model where links push values onto a path, and navigationDestination maps values to views.
Fix 2: Use isActive Binding with State Reset
Two key fixes here: binding isActive to a @State variable so SwiftUI properly tracks and resets the navigation state, and using .navigationViewStyle(.stack) to force single-column navigation instead of the split-view default on iPad.
Fix 3: Force Stack Navigation Style
On iPad, NavigationView defaults to a DoubleColumnNavigationViewStyle (sidebar + detail). The detail column retains its view, making the link appear "stuck." Forcing .stack style makes it behave like iPhone navigation.
Fix 4: Programmatic Navigation with NavigationStack
Using NavigationPath gives you full programmatic control. You can push, pop, and reset the navigation stack at any time, which completely avoids the one-time activation issue.
Fix 5: List Selection Pattern
For iPad split-view apps, NavigationSplitView (iOS 16+) with a selection binding is the correct pattern. It handles sidebar-detail navigation without the one-time link bug.
Common Pitfalls
- Using
NavigationViewon iPad without.stackstyle: The default double-column style on iPad is the primary cause of the one-time activation bug. Always add.navigationViewStyle(.stack)if you want push navigation, or migrate toNavigationStack/NavigationSplitView. - Nesting multiple
NavigationViews: Placing aNavigationViewinside anotherNavigationView(e.g., in a child view) causes unpredictable navigation behavior including links that stop working. Only the root view should contain the navigation container. - Not storing the
NavigationPath: When usingNavigationStack(path:), the path must be stored in@Stateor an@ObservableObject. If the path is recreated on each render, navigation resets unexpectedly. - Mixing
NavigationLinkstyles: Using both the oldNavigationLink(destination:)and the new value-basedNavigationLink(value:)in the sameNavigationStackcan cause conflicts. Pick one pattern consistently. - Forgetting
Hashableconformance: Value-basedNavigationLinkandnavigationDestinationrequire the value type to conform toHashable. Missing conformance causes a compile error or silent navigation failure.
Summary
- The "NavigationLink works only once" bug is caused by
NavigationView's split-view behavior on iPad - Add
.navigationViewStyle(.stack)to force single-column navigation as a quick fix - Migrate to
NavigationStack(iOS 16+) for reliable push navigation without state bugs - Use
NavigationSplitViewfor iPad apps that need sidebar-detail layout - Bind navigation state to
@Statevariables orNavigationPathfor full programmatic control - Never nest multiple
NavigationVieworNavigationStackcontainers

