Skip to content

Use Location

Overview

use_location is a ReactPy hook that returns the URL the browser is currently showing.

location = use_location()

Your components run on the server and know nothing about the browser unless you ask. use_location is how you ask: it reports the page the visitor is looking at, so navigation, breadcrumbs, and query-driven views can be built from it.


Reference

use_location()

Call it at the top level of a component. It takes no arguments.

from reactpy import use_location

Returns

A reactpy.types.Location — a plain dataclass with two fields, both required:

  • path: The URL path, mirroring window.location.pathname. For example "/reports/2024/q1".
  • query_string: The query string, mirroring window.location.search — including the leading "?". For example "?tab=2&user=ada", or "" when there is no query.

It is mutable and compares by value. Import the type with from reactpy.types import Location; it is not exported from the reactpy namespace.

Caveats

  • The "?" is part of the value. parse_qs(location.query_string) mis-parses the first parameter, producing a key "?tab" instead of "tab". Strip it: parse_qs(location.query_string.lstrip("?")). See the example below.
  • It is not the ASGI scope's path. use_scope()["path"] is the ReactPy websocket endpoint — "/reactpy/" — for every component on every page. use_location().path is the page the visitor is on. Verified in Chrome: visiting /some/deep/page?tab=2&user=ada gives Location(path='/some/deep/page', query_string='?tab=2&user=ada') while the scope still says "/reactpy/".
  • The browser reports it, so it is untrusted. The value arrives as parameters on the websocket URL and ReactPy does not validate it against your routes. Treat it as input: match it, never interpolate it into a "href" or a filesystem path without checking.
  • It needs a live connection. Called in a test's plain Layout render, it raises RuntimeError: No backend established a connection.. See working without a connection.
  • It does not exist under client-side rendering. With ReactPyCsr, Python runs in the browser but no ReactPy connection object is established, so the hook raises the same RuntimeError. Verified in Chrome against a CSR app. Read window.location through JavaScript there instead.
  • Like every hook, call it only at the top level — never in a loop, a conditional, or after an early return.

Usage

Reading query parameters

query_string arrives exactly as the browser shows it, which means the leading "?" is attached to the first parameter's name. That is the one detail that trips people up.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@component
def product_page():
    location = use_location()

    # `query_string` mirrors `window.location.search`, so it *keeps* the
    # leading "?". Strip it first or the first parameter gets a mangled name:
    # parse_qs("?tab=2&sort=price") yields the key "?tab", not "tab".
    params = parse_qs(location.query_string.lstrip("?"))
    tab = params.get("tab", ["overview"])[0]
    sort = params.get("sort", ["name"])[0]

    return html.div(
        html.h1(f"Products — {tab}"),
        html.p(f"sorted by: {sort}"),
        html.p(f"raw query string: {location.query_string!r}"),
        html.p(f"path: {location.path}"),
    )
1
# TODO

With ?tab=sales&sort=price this renders "Products — sales", sorted by price. With no query string at all it falls back to the defaults.

Building navigation from the current path

Splitting the path gives you the trail from the site root to wherever the visitor is.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@component
def breadcrumb():
    location = use_location()

    # `path` mirrors `window.location.pathname`, so a component rendered on the
    # ReactPy server can describe the page the browser is actually on -- useful
    # for navigation that needs to know where it is.
    parts = [part for part in location.path.split("/") if part]

    return html.nav(
        html.a({"href": "/"}, "home"),
        *[
            html.span(" / ", html.a({"href": "/" + "/".join(parts[: i + 1])}, part))
            for i, part in enumerate(parts)
        ],
    )
1
# TODO

On /reports/2024/q1 this produces links to /reports, /reports/2024, and /reports/2024/q1. Build the URLs yourself rather than pasting path segments in raw if they can contain anything you did not choose.

Working without a connection

A component that reads the location should say what it shows when there is none — a unit test, a reactpy_to_string call, or a CSR page all take this path.

@component
def page_title():
    try:
        location = use_location()
    except RuntimeError:
        return html.h1("(no browser yet)")
    return html.h1(f"at {location.path}")

Verified both ways through Layout: with no provider it renders the fallback; with one it renders the path.


Troubleshooting

My query parameters never match

The leading "?". parse_qs("?tab=2") gives {"?tab": ["2"]}. Strip it before parsing.

It says /reactpy/

You are reading use_scope()["path"], which is the websocket endpoint. Use use_location().path for the page.

It raises in my tests

Expected: a bare Layout render has no connection. Either wrap the call as shown above, or inject one — see use_connection.

It raises inside a ReactPyCsr app

Client-side rendering establishes no connection, so there is nothing to report. Read the URL from JavaScript instead, or move that component server-side.

Authors: Archmonger