Skip to main content

shiny-querynav

A Py Shiny extension that synchronizes navigation components with URL search parameters, enabling shareable and bookmarkable navigation states.

Features

  • Bidirectional sync: Navigation selections automatically sync with URL search parameters
  • Shareable URLs: Users can share links with specific navigation states (e.g., ?tab=profile)
  • Bookmarkable: Navigation states persist in browser bookmarks
  • Clean history: Uses replaceState() for seamless URL updates without cluttering browser history
  • Simple API: Just two functions - dependency() and sync()

Installation

pip install shiny-querynav

Or with uv:

uv add shiny-querynav

Quick Start

from shiny import App, ui
from shiny_querynav import querynav

app_ui = ui.page_fluid(
    ui.navset_bar(
        querynav.dependency(),  # Required: registers the JS handler
        ui.nav_panel("Home", "Welcome!", value="home"),
        ui.nav_panel("Profile", "User profile", value="profile"),
        ui.nav_panel("Settings", "App settings", value="settings"),
        id="main_nav",
        title="My App"
    )
)

def server(input, output, session):
    # Sync navigation with URL parameter "tab"
    querynav.sync("main_nav", param_name="tab")

app = App(app_ui, server)

Now your app URLs will update automatically:

  • Clicking "Home" → ?tab=home
  • Clicking "Profile" → ?tab=profile
  • Clicking "Settings" → ?tab=settings

And visiting /?tab=profile will automatically select the Profile panel!

How It Works

1. Include the dependency

Add querynav.dependency() to your UI (typically within your navigation component):

ui.navset_bar(
    querynav.dependency(),  # Add this
    # ... your nav panels
)

2. Sync your navigation

In your server function, call sync() with your navigation ID:

def server(input, output, session):
    querynav.sync("main_nav", param_name="tab")

That's it! The extension handles the rest automatically.

API Reference

querynav.dependency()

Creates the HTML dependency required for shiny-querynav to work.

Returns: HTMLDependency

Usage: Include this in your UI, typically within a navigation component.

ui.navset_bar(
    querynav.dependency(),
    # ... panels
)

querynav.sync(nav_id, *, param_name=None, home_value=None)

Synchronizes a navigation component with URL search parameters.

Parameters:

  • nav_id (str): The ID of the navigation component (matches the id parameter in ui.navset_bar(), ui.navset_tab(), etc.)
  • param_name (str, optional): The URL search parameter name. Defaults to nav_id if not provided.
  • home_value (str, optional): The navigation value representing the "home" or default page. When this panel is selected, the search parameter is removed from the URL for a cleaner appearance (e.g., / instead of /?tab=home).

Returns: None

Usage: Call this from within your server function.

def server(input, output, session):
    # Use default param name (same as nav_id)
    querynav.sync("main_nav")  # URL: ?main_nav=value

    # Or specify a custom param name
    querynav.sync("main_nav", param_name="tab")  # URL: ?tab=value

    # Keep home page URL clean (no query parameter)
    querynav.sync("main_nav", param_name="tab", home_value="home")
    # URL: / for home, ?tab=about for other pages

Examples

Basic Example

from shiny import App, ui
from shiny_querynav import querynav

app_ui = ui.page_fluid(
    ui.navset_bar(
        querynav.dependency(),
        ui.nav_panel("Panel A", "Content A", value="a"),
        ui.nav_panel("Panel B", "Content B", value="b"),
        id="page"
    )
)

def server(input, output, session):
    querynav.sync("page", param_name="p")

app = App(app_ui, server)

Clean Home Page URLs

Keep your home page URL clean by removing the search parameter for the default panel:

from shiny import App, ui
from shiny_querynav import querynav

app_ui = ui.page_fluid(
    ui.navset_bar(
        querynav.dependency(),
        ui.nav_panel("Home", "Welcome!", value="home"),
        ui.nav_panel("About", "About us", value="about"),
        ui.nav_panel("Contact", "Get in touch", value="contact"),
        id="nav",
        title="My Site"
    )
)

def server(input, output, session):
    # Home page will have clean URL: /
    # Other pages will show: ?page=about or ?page=contact
    querynav.sync("nav", param_name="page", home_value="home")

app = App(app_ui, server)

Result:

  • Home page: / (clean, no query parameter)
  • About page: /?page=about
  • Contact page: /?page=contact
  • Visiting / automatically selects "Home" panel

Multiple Navigation Components

You can sync multiple navigation components with different parameters:

app_ui = ui.page_fluid(
    ui.navset_bar(
        querynav.dependency(),
        ui.nav_panel("Home", value="home"),
        ui.nav_panel("Dashboard", value="dash"),
        id="main_nav",
        title="App"
    ),
    ui.navset_tab(
        ui.nav_panel("Overview", value="overview"),
        ui.nav_panel("Details", value="details"),
        id="sub_nav"
    )
)

def server(input, output, session):
    querynav.sync("main_nav", param_name="page")
    querynav.sync("sub_nav", param_name="view")

# URL might look like: ?page=dash&view=details

With Different Navigation Types

Works with all Shiny navigation components:

# With navset_tab
ui.navset_tab(
    querynav.dependency(),
    ui.nav_panel("Tab 1", value="t1"),
    ui.nav_panel("Tab 2", value="t2"),
    id="tabs"
)

# With navset_pill
ui.navset_pill(
    querynav.dependency(),
    ui.nav_panel("Pill 1", value="p1"),
    ui.nav_panel("Pill 2", value="p2"),
    id="pills"
)

# With navset_card_tab
ui.navset_card_tab(
    querynav.dependency(),
    ui.nav_panel("Card 1", value="c1"),
    ui.nav_panel("Card 2", value="c2"),
    id="cards"
)

How It Works (Technical Details)

The extension uses Shiny's reactive system and custom message handlers:

  1. Navigation → URL: When a user clicks a navigation panel:

    • A reactive effect detects the change
    • The server sends a custom message to the JavaScript handler
    • JavaScript updates the URL using the URL and URLSearchParams APIs
    • The URL is updated with window.history.replaceState() (no page reload)
  2. URL → Navigation: When the page loads or the URL changes:

    • A reactive effect monitors session.clientdata.url_search()
    • If matching search parameters are found, ui.update_navset() is called
    • The navigation component updates to reflect the URL state

Requirements

  • Python 3.12+
  • Shiny 1.5.0+

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Author

Kenji Sato (mail@kenjisato.jp)

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

shiny_querynav-0.1.0.tar.gz (5.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

shiny_querynav-0.1.0-py3-none-any.whl (6.7 kB view details)

Uploaded Python 3

File details

Details for the file shiny_querynav-0.1.0.tar.gz.

File metadata

  • Download URL: shiny_querynav-0.1.0.tar.gz
  • Upload date:
  • Size: 5.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for shiny_querynav-0.1.0.tar.gz
Algorithm Hash digest
SHA256 939ef2762bcb6528dd9afd0b3023f2ce2bc1684dfa86a6be70ac3c96ef08a666
MD5 7b858214ca1b386bc8ce958195958d37
BLAKE2b-256 e048d80620089a6fc819db43040f43e54b9427798758877a5f944a9a1f73b156

See more details on using hashes here.

Provenance

The following attestation bundles were made for shiny_querynav-0.1.0.tar.gz:

Publisher: publish.yml on kenjisato/shiny-querynav

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shiny_querynav-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: shiny_querynav-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 6.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for shiny_querynav-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fa34779ff09a73a7b2b4505a6de7a90c1badaa3c22b0833c1499fa6621236148
MD5 51dcc3f5e30e861897a889eb4ebfaae8
BLAKE2b-256 2f67a31e50089eabbe6a30aaa693e8ef3dc93e7a420177f895a75d3902685b9e

See more details on using hashes here.

Provenance

The following attestation bundles were made for shiny_querynav-0.1.0-py3-none-any.whl:

Publisher: publish.yml on kenjisato/shiny-querynav

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page