Skip to main content

A Py Shiny extension that synchronizes navigation components with URL search parameters

Project description

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)

Project details


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.

Supported by

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