Navigation¶
Three small examples that show off the available navigators (stack,
tab, drawer) and how to navigate between screens with
use_navigation and read the current
route with use_route.
For the conceptual model and the full API, see the Navigation guide and the Navigation API reference.
Run it¶
Create a project with pn init navigation-demo, then cd navigation-demo.
Replace app/main.py with the Stack, Tab, or Drawer snippet below.
Each includes its imports and defines App, the project's root component.
From the project root, run pn preview to open the app in your browser.
To run on a device or simulator, leave the preview running and use
pn run android or pn run ios in another terminal from the same
directory. See the Browser preview guide
and Development workflow for more options.
The Nesting and Focus-aware effects snippets need additional definitions; follow the note at the end of each section to try them.
Stack navigator¶
A pushable, poppable stack. The default for "go from screen A to screen B with a back button".
import pythonnative as pn
Stack = pn.create_stack_navigator()
@pn.component
def HomeScreen():
nav = pn.use_navigation()
return pn.Column(
pn.Text("Home", style={"font_size": 28, "bold": True}),
pn.Button(
"View profile",
on_press=lambda: nav.navigate("Profile", user_id=42),
),
style={"spacing": 12, "padding": 16},
)
@pn.component
def ProfileScreen():
route = pn.use_route()
nav = pn.use_navigation()
return pn.Column(
pn.Text(f"User #{route.params['user_id']}", style={"font_size": 24}),
pn.Button("Back", on_press=nav.go_back),
style={"spacing": 12, "padding": 16},
)
@pn.component
def App():
return pn.NavigationContainer(
Stack.Navigator(
Stack.Screen("Home", HomeScreen, title="Home"),
Stack.Screen("Profile", ProfileScreen, title="Profile"),
)
)
nav.navigate("Profile", user_id=42) pushes onto the stack (or
returns to an existing Profile entry); nav.push(...) always adds a
new one; nav.go_back() pops one frame. To replace the entire stack
(e.g., after login), use nav.reset("Home").
Tab navigator¶
A persistent tab bar at the bottom (iOS) or top (Android), with one screen per tab. Each tab keeps its own state across switches.
import pythonnative as pn
Tabs = pn.create_tab_navigator()
@pn.component
def Feed():
return pn.Text("Feed", style={"padding": 16, "font_size": 24})
@pn.component
def Search():
q, set_q = pn.use_state("")
return pn.Column(
pn.TextInput(value=q, on_change=set_q, placeholder="Search..."),
pn.Text(f"Results for: {q}"),
style={"spacing": 12, "padding": 16},
)
@pn.component
def Settings():
return pn.Text("Settings", style={"padding": 16, "font_size": 24})
@pn.component
def App():
return pn.NavigationContainer(
Tabs.Navigator(
Tabs.Screen(name="Feed", component=Feed),
Tabs.Screen(name="Search", component=Search),
Tabs.Screen(name="Settings", component=Settings),
)
)
The search box keeps its query when the user switches to Settings and back; tab screens are not unmounted on blur unless the navigator is configured otherwise.
Drawer navigator¶
A side drawer for primary navigation in larger apps.
import pythonnative as pn
Drawer = pn.create_drawer_navigator()
@pn.component
def Inbox():
return pn.Text("Inbox", style={"padding": 16, "font_size": 24})
@pn.component
def Sent():
return pn.Text("Sent", style={"padding": 16, "font_size": 24})
@pn.component
def App():
return pn.NavigationContainer(
Drawer.Navigator(
Drawer.Screen(name="Inbox", component=Inbox),
Drawer.Screen(name="Sent", component=Sent),
)
)
The drawer opens via a swipe from the leading edge or
nav.toggle_drawer() from inside any screen.
Nesting¶
Navigators compose. A typical pattern is a tab navigator at the top with a stack navigator inside each tab:
@pn.component
def App():
return pn.NavigationContainer(
Tabs.Navigator(
Tabs.Screen(name="Home", component=HomeStack),
Tabs.Screen(name="Profile", component=ProfileStack),
)
)
@pn.component
def HomeStack():
return Stack.Navigator(
Stack.Screen(name="Feed", component=Feed),
Stack.Screen(name="Post", component=Post),
)
Pushing onto the inner stack leaves the tab bar visible; switching tabs preserves each stack's own history.
This snippet sketches the composition rather than a complete app.
To try it, start from the Tab navigator example, keeping its import,
Tabs, and Feed. Add Stack = pn.create_stack_navigator(), a Post
screen, and a ProfileStack component that returns a Stack.Navigator
with its own screens. Add HomeStack and replace the Tab example's
App with the one above, which wraps the navigators in
pn.NavigationContainer. Then follow the same run instructions.
Focus-aware effects¶
When you need to start something only while a screen is on screen
(camera, GPS, animation), use
use_focus_effect:
@pn.component
def CameraScreen():
pn.use_focus_effect(
lambda: (start_camera(), stop_camera)[1],
deps=[],
)
return pn.View()
The cleanup runs as soon as the user navigates away, even if the screen stays mounted.
To try this with the Tab navigator example, keep its import and App,
add CameraScreen and stand-ins for start_camera and stop_camera,
and replace Tabs.Screen(name="Settings", component=Settings) with
Tabs.Screen(name="Camera", component=CameraScreen). Run pn preview,
then switch between the Camera and Feed tabs to trigger the effect
and its cleanup.
Next steps¶
- Reference: Navigation API.
- Patterns and lifecycle: Navigation guide.
- Render lists inside a tab: Lists.