PyxlePyxle/Docs

Client API Reference

All client-side components and hooks are importable from pyxle/client:

import {
  Head, Script, Image, ClientOnly,
  Form, useAction,
  Link, navigate, prefetch, refresh, usePathname
} from 'pyxle/client';

Components

Manages document <head> elements during server-side rendering.

<Head>
  <title>Page Title</title>
  <meta name="description" content="Description" />
</Head>

Props: Children only (standard React children).

Behaviour:

  • Renders null in the DOM
  • During SSR, extracts children markup and registers head elements
  • Elements are merged and deduplicated with the HEAD Python variable and layout head blocks
  • Can be used in any component (page, layout, or nested)

<Script>

Loads external scripts with configurable loading strategies.

<Script src="https://analytics.example.com/script.js" strategy="afterInteractive" />

Props:

Prop Type Default Description
src string (required) Script URL
strategy string "afterInteractive" When to load the script
async boolean false HTML async attribute
defer boolean false HTML defer attribute
onLoad () => void -- Callback on successful load
onError () => void -- Callback on load failure

Strategies:

Value Description
"beforeInteractive" Injected in <head> before hydration (render-blocking)
"afterInteractive" Loaded after React hydration (default)
"lazyOnload" Loaded during browser idle time

Behaviour: Renders null. The SSR pipeline extracts <Script> declarations and handles loading according to the strategy.


<Image>

Renders an <img> tag with automatic lazy loading.

<Image src="/hero.jpg" alt="Hero" width={1200} height={630} />

Props:

Prop Type Default Description
src string (required) Image source URL
alt string "" Alt text for accessibility
width number -- Image width in pixels
height number -- Image height in pixels
priority boolean false Eager loading (above-the-fold images)
lazy boolean true Lazy loading (below-the-fold images)

Behaviour: Renders a standard <img> with loading="eager" when priority is true, loading="lazy" otherwise. All additional props are forwarded to the <img> element.


<ClientOnly>

Renders children only on the client after hydration.

<ClientOnly fallback={<p>Loading...</p>}>
  <MapWidget />
</ClientOnly>

Props:

Prop Type Default Description
children ReactNode -- Content to render on the client
fallback ReactNode null Placeholder shown during SSR

Behaviour: Returns fallback during SSR and on first client render. After useEffect fires (hydration complete), switches to rendering children. Prevents hydration mismatch for browser-only content.


<Form>

Progressive-enhancement form component for calling server actions.

<Form action="create_post" onSuccess={(data) => alert(`Created: ${data.id}`)}>
  <input name="title" required />
  <button type="submit">Create</button>
</Form>

Props:

Prop Type Default Description
action string (required) Name of the @action function
pagePath string current page Page where the action is defined
onSuccess (data) => void -- Called with response data on success
onError (message) => void -- Called with error message on failure
resetOnSuccess boolean true Reset form fields after success
children ReactNode -- Form contents

Behaviour:

  • With JavaScript: intercepts submit, serialises form data to JSON, POSTs to the action endpoint
  • Without JavaScript: falls back to a standard HTML form POST
  • Displays inline error messages on failure
  • Automatically resolves the action endpoint URL
  • All additional props are forwarded to the <form> element

Client-side navigation link that prevents full page reloads.

<Link href="/about">About Us</Link>

Imported from pyxle/client. Renders an <a> tag that intercepts clicks for client-side navigation.


Hooks

useAction(actionName, options?)

Hook for calling server actions programmatically.

const deleteItem = useAction('delete_item');

async function handleDelete(id) {
  const result = await deleteItem({ id });
  if (result.ok) {
    console.log('Deleted');
  }
}

Parameters:

Parameter Type Description
actionName string Name of the @action function
options.pagePath string? Page where the action is defined (defaults to current page)
options.onMutate (payload) => void Called immediately before the request (for optimistic updates)

Returns: An async function with attached state properties.

Return value properties:

Property Type Description
.pending boolean true while request is in flight
.error string | null Error message on failure
.data object | null Last successful response data

Calling the returned function:

const result = await actionFn(payload);
// result.ok: boolean
// result.error?: string
// result.*: response data fields

Behaviour:

  • New calls abort previous in-flight requests
  • State resets on each new call
  • onMutate fires synchronously before the fetch (use for optimistic UI)

usePathname()

Reactive hook that returns the current URL pathname and re-renders on client-side navigation.

import { usePathname, Link } from 'pyxle/client';

function NavLink({ href, children }) {
  const pathname = usePathname();
  const active = pathname === href;
  return (
    <Link href={href} className={active ? 'text-emerald-400' : 'text-zinc-400'}>
      {children}
    </Link>
  );
}

Returns: string — the current pathname (e.g. /dashboard/settings).

Behaviour:

  • Reads window.location.pathname on the client
  • During SSR, returns the path currently being rendered (via globalThis.__PYXLE_CURRENT_PATHNAME__) so the first client render matches — no hydration mismatch
  • Subscribes to framework navigation events (Link, navigate(), refresh(), popstate) and re-renders on change

Functions

Trigger client-side navigation programmatically.

import { navigate } from 'pyxle/client';

navigate('/dashboard');

prefetch(path)

Prefetch a page's data and assets.

import { prefetch } from 'pyxle/client';

<button onMouseEnter={() => prefetch('/dashboard')}>
  Go to Dashboard
</button>

refresh()

Re-run the current page's @server loader and re-render with fresh data. Does not reload the page or change scroll position.

import { refresh } from 'pyxle/client';

<button onClick={() => refresh()}>
  Refresh data
</button>

invalidate(url?)

Drop a URL from the client-side navigation cache so the next navigate(url) refetches the loader payload instead of replaying the cached one. Call this after a mutation (create, update, delete) that affects a list view the user might navigate back to.

Without an argument, clears every cached entry. Returns true if an entry was evicted.

import { invalidate, navigate } from 'pyxle/client';

async function handleDelete(id) {
  await deletePost({ id });
  invalidate('/posts');      // drop the cached /posts list
  navigate('/posts');         // next visit refetches
}

Related: server-driven invalidation. Your @action can tell the client which URLs to invalidate via the `invalidate_routes()` helper. Responses carrying an x-pyxle-invalidate header are honoured automatically by useAction and <Form>, so most apps never call invalidate() in client code directly.

Client-side loader payloads are cached per URL for 30 seconds by default so back/forward navigation is instant while data stays reasonably fresh. Tune the cap by setting a global on the window before Pyxle's client runtime boots (e.g. in a <Script strategy="beforeInteractive"> block):

<Script strategy="beforeInteractive">
  {`window.__PYXLE_NAV_STALE_MS__ = 60000;`}  {/* 60s */}
</Script>

Useful values:

  • 0 — never cache; every navigation hits the server.
  • 30000 (default) — matches Next.js App Router's heuristic.
  • a large number — cache for the lifetime of the tab.

For per-mutation control, prefer `invalidate(url)` or the x-pyxle-invalidate response header over global TTL tuning.