Skip to content

fix: sync query_params on browser back/forward navigation#9490

Open
kimjune01 wants to merge 5 commits into
marimo-team:mainfrom
kimjune01:fix/query-params-browser-navigation
Open

fix: sync query_params on browser back/forward navigation#9490
kimjune01 wants to merge 5 commits into
marimo-team:mainfrom
kimjune01:fix/query-params-browser-navigation

Conversation

@kimjune01
Copy link
Copy Markdown

Fixes #4153

When users navigate with browser back/forward buttons, the URL changes but mo.query_params doesn't update, leaving cells stale. The frontend tracks URL changes via pushState when cells update query params, but the reverse path (browser navigation → kernel update) was missing.

Fix

Add a popstate listener in useMarimoKernelConnection that parses the new query params and sends them to the kernel via a new update_query_params endpoint. The kernel clears and updates its query_params state, then re-runs dependent cells.

Backend wiring follows the same pattern as set_ui_element_value@requires("read") permission, command dispatched through the kernel's control request handler.

Fixes marimo-team#4153. When users navigate using browser back/forward buttons,
query_params now updates and dependent cells re-execute.

Changes:
- Add UpdateQueryParamsCommand to sync URL state with kernel
- Add popstate listener to detect browser navigation
- Add /api/kernel/update_query_params endpoint
- Update QueryParams state triggers cell re-execution via State system

The fix leverages marimo's existing State system: when the browser URL
changes via popstate, the frontend sends the new params to the kernel,
which updates the QueryParams state object, triggering re-execution of
cells that depend on query_params.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 10, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment May 19, 2026 4:14pm

Request Review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 10, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 11 files

Architecture diagram
sequenceDiagram
    participant Browser as Browser (User)
    participant FE as Frontend Runtime (useMarimoKernelConnection)
    participant Router as URL Router (pushState/popstate)
    participant RequestClient as Request Client (sendUpdateQueryParams)
    participant API as API Server (/api/kernel/update_query_params)
    participant Kernel as Kernel Runtime (control handler)
    participant QueryParams as QueryParams State
    participant Scheduler as Cell Scheduler

    Note over Browser,Scheduler: Browser Back/Forward Navigation → Query Param Sync

    Browser->>Router: browser back/forward click
    Router->>Router: URL changes (popstate event)
    Router->>FE: popstate event fires
    FE->>FE: parseQueryParams() from window.location.href
    FE->>RequestClient: sendUpdateQueryParams({ queryParams: {...} })
    
    alt Static/Wasm (Islands or Pyodide)
        RequestClient->>RequestClient: putControlRequest(type: "update-query-params")
    else Server mode
        RequestClient->>API: POST /api/kernel/update_query_params
        API->>API: @requires("read") permission check
        API->>Kernel: dispatchControlRequest(UpdateQueryParamsRequest)
    end

    Kernel->>Kernel: handle_update_query_params()
    Kernel->>QueryParams: clear() + update(request.query_params)
    QueryParams->>QueryParams: _set_value() triggers state change
    QueryParams->>Scheduler: state update notification
    Scheduler->>Scheduler: _run_cells(set()) re-executes dependent cells
    Scheduler->>Kernel: broadcastNotification(CompletedRunNotification)
    Kernel-->>API: SuccessResponse
    API-->>RequestClient: 200 OK
    RequestClient-->>FE: null (void)
    FE-->>Browser: UI updates with new cell values

    Note over Browser,Kernel: Same pattern as set_ui_element_value
Loading

@kimjune01
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

… wiring

The new update_query_params endpoint was missing from the OpenAPI schema
generation (MODELS list) and the generated TypeScript types. Also missing
from the frontend mock, lazy-request proxy, and toasting wrapper—causing
TypeScript build failures.
@github-actions github-actions Bot added the bash-focus Area to focus on during release bug bash label May 12, 2026
Resolves conflict with PR marimo-team#9577 (refactor: split kernel command dispatch
into router + callback bundles). The request_handler cached_property was
extracted from runtime.py into kernel_request_handlers.py — wire the
UpdateQueryParamsCommand through the new KernelRequestHandlers.register
path instead of the old in-line handler.register block.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes stale mo.query_params() state when users navigate via browser back/forward by adding a frontend popstate listener that sends the current URL query params to the kernel, plus a new backend control endpoint/command to apply the update and re-run dependent cells.

Changes:

  • Add /api/kernel/update_query_params endpoint plus OpenAPI schema/types for an UpdateQueryParams* request/command.
  • Register and handle a new UpdateQueryParamsCommand in the kernel control request router to update query param state and trigger re-execution.
  • Add a frontend popstate listener and wire sendUpdateQueryParams through all request clients/bridges (network, wasm, islands, mocks).

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/openapi/src/api.ts Adds generated TS types for the new update_query_params endpoint and schemas.
packages/openapi/api.yaml Defines OpenAPI path + schemas for updating query params from browser navigation.
marimo/_server/models/models.py Adds UpdateQueryParamsRequest model to convert request payload into a runtime command.
marimo/_server/api/endpoints/execution.py Exposes /update_query_params endpoint and dispatches it as a control request.
marimo/_runtime/kernel_request_handlers.py Registers/implements kernel-side handler for UpdateQueryParamsCommand.
marimo/_runtime/commands.py Introduces UpdateQueryParamsCommand and adds it to the command union.
marimo/_cli/development/commands.py Includes new command/request in schema generation list.
frontend/src/core/websocket/useMarimoKernelConnection.tsx Adds popstate listener and sends query param updates to the backend.
frontend/src/core/wasm/bridge.ts Implements sendUpdateQueryParams for the Pyodide bridge.
frontend/src/core/network/types.ts Extends RunRequests and exports UpdateQueryParamsRequest type.
frontend/src/core/network/requests-toasting.tsx Adds toast message mapping for sendUpdateQueryParams errors.
frontend/src/core/network/requests-static.ts Adds static-mode no-op implementation for sendUpdateQueryParams.
frontend/src/core/network/requests-network.ts Adds network request implementation calling /api/kernel/update_query_params.
frontend/src/core/network/requests-lazy.ts Ensures sendUpdateQueryParams waits for connection open like other run requests.
frontend/src/core/kernel/queryParamHandlers.ts Adds parseQueryParams() helper used by the new popstate path.
frontend/src/core/islands/bridge.ts Implements sendUpdateQueryParams for islands/worker bridge.
frontend/src/mocks/requests.ts Updates request client mocks to include sendUpdateQueryParams.
Comments suppressed due to low confidence (1)

marimo/_runtime/kernel_request_handlers.py:145

  • This handler always clears/updates query_params and calls _set_value, which registers a state update even when the incoming params are identical, triggering unnecessary cell re-execution. Compare the current params to request.query_params and early-return (or skip _set_value/_run_cells) when there is no change.
        k = self._kernel
        k.query_params._params.clear()
        k.query_params._params.update(request.query_params)
        k.query_params._set_value(k.query_params._params)
        if k.state_updates:
            await k._run_cells(set())
        broadcast_notification(CompletedRunNotification())

Comment on lines +140 to +141
k.query_params._params.clear()
k.query_params._params.update(request.query_params)
Comment on lines +45 to +59
const url = new URL(window.location.href);
const params: Record<string, string | string[]> = {};

for (const [key, value] of url.searchParams.entries()) {
const existing = params[key];
if (existing === undefined) {
params[key] = value;
} else if (Array.isArray(existing)) {
existing.push(value);
} else {
params[key] = [existing, value];
}
}

return params;
Comment on lines +549 to +562
// Listen for browser back/forward navigation to update query params
useEffect(() => {
const handlePopState = () => {
const queryParams = parseQueryParams();
// Import at runtime to avoid circular dependency
import("../network/requests").then(({ getRequestClient }) => {
const client = getRequestClient();
void client.sendUpdateQueryParams({ queryParams });
});
};

window.addEventListener("popstate", handlePopState);
return () => window.removeEventListener("popstate", handlePopState);
}, []);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bash-focus Area to focus on during release bug bash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Notebooks using query_params do not update on browser navigation changes

2 participants