use-python-query
v0.2.0
Published
Run Python functions in the browser via Pyodide web workers, with TanStack Query-style React hooks.
Downloads
30
Maintainers
Readme
use-python-query
Run Python functions in the browser with Pyodide (WebAssembly),
offloaded to a web worker so the main thread never blocks. Exposed as React hooks
with a TanStack Query-style API: usePythonQuery for
data fetching and usePythonMutation for mutations.
Install
npm install use-python-query
# or
pnpm add use-python-query
# or
yarn add use-python-queryreact >= 18 is a peer dependency. Pyodide is bundled into the worker for you,
so you don't need to install it.
Queries
import { usePythonQuery } from "use-python-query";
const pythonSource = `
def greet(name):
return f"Hello, {name}!"
def double(n):
return n * 2
`;
function Greeter({ name }: { name: string }) {
const { data, isPending, isError, error, refetch } = usePythonQuery<
string,
[string]
>({
source: pythonSource,
functionName: "greet",
variables: [name],
staleTime: 30_000,
});
if (isPending) return <p>Running Python…</p>;
if (isError) return <p>Error: {error?.message}</p>;
return (
<p>
{data} <button onClick={() => refetch()}>Again</button>
</p>
);
}Options
| Option | Type | Default | Description |
| ----------------------------- | --------------------- | ------- | --------------------------------------------------------------- |
| source | string | — | Python code defining the function. |
| functionName | string | — | Python function to call. |
| variables | unknown[] | [] | Positional arguments passed to the Python function. |
| queryKey | unknown[] | — | Cache identity. Defaults to source + function + variables. |
| enabled | boolean | true | When false, the query never runs. |
| staleTime | number | 0 | Time (ms) data is considered fresh; only stale data refetches. |
| gcTime | number | 5min | How long a key stays cached after the last subscriber unmounts. |
| retry | boolean \| number | 0 | Retry failed fetches (with exponential backoff). |
| retryDelay | number | 1000 | Base retry delay in ms. |
| refetchInterval | number \| false | false | Automatically refetch every N ms. |
| refetchIntervalInBackground | boolean | false | Keep interval refetching while the tab is hidden. |
| refetchOnWindowFocus | boolean \| "always" | true | Refetch (when stale) on window focus. |
| pyodideIndexURL | string | CDN | Override where Pyodide assets are loaded from. |
Mutations
import { usePythonMutation } from "use-python-query";
function Doubler() {
const { data, error, isPending, mutate } = usePythonMutation<
number,
[number]
>({
source: "def double(n):\n return n * 2\n",
functionName: "double",
onSuccess: (result) => {
console.log(`Result: ${result}`);
},
});
return (
<div>
<button onClick={() => mutate(21)} disabled={isPending}>
{isPending ? "Running Python…" : "Double"}
</button>
{error && <p>Error: {error.message}</p>}
{data !== undefined && <p>{data}</p>}
</div>
);
}Framework-agnostic client
import { invokePython } from "use-python-query";
const result = await invokePython<number>({
source: "def add(a, b): return a + b",
functionName: "add",
variables: [2, 3], // -> 5
});Configuration
import { configurePython } from "use-python-query";
// Point Pyodide at self-hosted assets instead of the CDN
configurePython({ pyodideIndexURL: "/pyodide/" });Cleanup
The worker boots Pyodide once (a ~10–20 MB WASM download on first use) and keeps it alive for the lifetime of the page. Call this when you want to release it:
import { terminatePythonWorkers } from "use-python-query";
terminatePythonWorkers();Pyodide assets / indexURL
Pyodide's JS is bundled into the worker. The .wasm, Python stdlib zip and other
assets are loaded from https://cdn.jsdelivr.net/npm/pyodide@{version}/ by
default (matching the bundled Pyodide version). To self-host, copy the dist
contents of the pyodide npm package into your static dir and set
pyodideIndexURL (globally via configurePython, or per-hook/per-call).
If your app is served under a sub-path, make sure to configure the index URL accordingly.
