Theme
Version
GitHub PyPI Discord
On this page

Browser APIs

Citry adds a small browser API around its component boundaries, Alpine runtime, and server events. This page covers the names Citry provides. For standard Alpine directives and magics, use the Alpine documentation.

Component JavaScript

$component

Register the JavaScript that belongs to one component class. Use $component inside Component.js or the file named by Component.js_file. Citry binds that registration to the Python component class, then calls its initializer for every live rendered instance.

The callback form accepts one initializer:

$component(({ els, data, scope }) => {
  scope.name = data.name;
  els[0].dataset.ready = "true";
});

The configuration form adds declared client props:

$component({
  props: {
    name: {
      type: String,
      required: true,
    },
  },
  init: ({ props, scope }) => {
    scope.name = props.name;
  },
});

A component class may register exactly one $component initializer. Citry runs it synchronously after the component boundary, parent initialization, and client props are ready. Do not return a Promise.

Return a function when the initializer creates something that must be cleaned up. Citry calls it before that instance initializes again and when the instance leaves the page:

$component(({ els }) => {
  const chart = createChart(els[0]);
  return () => chart.destroy();
});

The initializer receives these values:

NameValue
idThe current server render ID. A rerender may replace it.
elsA stable array containing the instance's current element roots. It is empty for a rootless component.
dataThe JSON value returned by js_data(), or null.
graphThe current ownership route and source metadata when the instance belongs to a client graph.
propsThe stable, reactive, top-level read-only values declared by the configuration form. The callback form receives an empty object.
scopeThe stable reactive object available to Alpine expressions inside this component.
stateThe component's reactive Events State, or null when the component declares no Events.
effect(fn)Run a managed reactive effect. It returns an early-stop function and stops automatically before cleanup.
reactive(value)Turn an object or array into an Alpine reactive proxy.
provide(key, value)Provide a value to rendered descendants during synchronous initialization.
inject(key, default?)Read the nearest inherited client value. Missing values throw unless a default was supplied.
unprovide(key)Hide an inherited value from rendered descendants during synchronous initialization.
sendEvent(name, args?, opts?)Call one of this component's declared server events and return a Promise for its data result.
onEvent(name, callback)Listen for server-dispatched events targeting this instance and return an unsubscribe function.

A prop declaration accepts type, required, and default. type may be a constructor or an array of constructors. required defaults to false. Use a factory for an object or array default so instances do not share one mutable value:

props: {
  filters: {
    type: Object,
    default: () => ({}),
  },
}

The Client interactivity page explains component boundaries, client props, slot scope, and rootless components. The JS and CSS dependencies page explains when component scripts load.

Alpine magics

Citry adds the following magics to Alpine expressions inside an active Citry component. The event-related magics act on the component instance that owns the expression. The context magics follow Citry's rendered ownership path, including slots and teleports.

$state

Read the component's reactive public Events State. Assigning a writable field queues that change for the component's next server call. A field excluded by State._public cannot be read, and a field excluded by State._model cannot be written.

<button @click="$state.count++">Add one</button>
<output x-text="$state.count"></output>

State travels through the browser and must be treated as client input. See Security.

$loading

Return whether this component has a queued or running server call. Pass an event name to check only that handler. An unknown handler name throws an error.

<button :disabled="$loading('save')">
  <span x-show="!$loading('save')">Save</span>
  <span x-show="$loading('save')">Saving...</span>
</button>

$error

Read the last failed call as { status, code, message, fieldErrors? }, or null when there is no current error. A successful call clears it. The value is read-only.

<p x-show="$error" x-text="$error?.message"></p>

$sendEvent

Call a declared server event from an Alpine expression:

$sendEvent(name, args?, opts?)

The method returns a Promise. It resolves with the handler's data result and rejects with a structured event error. opts.timeout overrides the request timeout. opts.wait: false lets a call bypass the component's event queue.

<button
  @click="result = await $sendEvent('preview', { page: 2 })"
>
  Preview page 2
</button>

$onEvent

Listen for server-dispatched events targeting this component instance:

const stop = $onEvent("cart:changed", (detail) => {
  console.log(detail);
});

The return value removes the listener. Use the onEvent member inside $component when the subscription should automatically share the component initializer's cleanup lifetime.

$provide

Provide one value to rendered descendants:

<section x-init="$provide('theme', { name: 'dark' })">
  <c-slot />
</section>

The key may be a non-empty string or a Symbol. Call $provide during synchronous directive initialization, normally from x-init. To change the value later, provide one reactive object and update its fields.

$inject

Read the nearest inherited value. Citry returns the exact value that was provided. A missing key throws unless you pass a default:

<output x-text="$inject('theme', 'system')"></output>

The helper is bound to the element where the magic is read. Its lookup follows Citry's rendered ownership path rather than relying only on physical DOM parents.

$unprovide

Hide an inherited value from rendered descendants:

<section x-init="$unprovide('theme')">
  <output x-text="$inject('theme', 'system')"></output>
</section>

Call $unprovide during synchronous directive initialization. A nearer $provide can establish the same key again for its own descendants.

See Provide and inject for shadowing, slot placement, reactive updates, and multi-placement behavior.

Page-wide APIs

Use these methods from page scripts and integrations rather than from one component's Alpine expressions.

Citry.alpine.beforeStart

Register an Alpine plugin before Citry starts its owned Alpine runtime:

Citry.alpine.beforeStart((Alpine) => {
  Alpine.plugin(myPlugin);
});

The callback receives Citry's pinned Alpine object. Calling beforeStart after startup has begun throws an error. Do not load a second Alpine build.

Citry.events.send

Call a server event on any interactive component instance:

Citry.events.send(target, name, args?, opts?)

target is a current render ID or an Element inside the target instance. The method returns the same kind of Promise as $sendEvent and the component initializer's sendEvent member.

Citry.events.on

Listen page-wide for a server-dispatched event:

const stop = Citry.events.on("cart:changed", (detail) => {
  updateHeader(detail);
});

The callback receives the event's detail. The returned function removes the listener. Unlike $onEvent, this listener is not limited to one component instance.

Citry.events.configure

Set page-wide defaults for later event calls:

Citry.events.configure({
  timeout: 45_000,
  url: "/citry/ext/events/",
});
OptionMeaning
csrfThe token source and request-header name. It accepts cookie, header, or a token string or function.
timeoutMilliseconds before a call rejects. The default is 30000.
transportThe registered transport name. The default is "fetch".
urlOverride the Events route base URL. Normally Citry reads it from the page manifest.

Citry.events.registerTransport

Register a page-wide event transport under a name:

Citry.events.registerTransport("custom", {
  send: async (envelope) => sendThroughHost(envelope),
});

The transport's send method receives one Citry event envelope and returns its result envelope or a Promise for it. Select the transport with Citry.events.configure({ transport: "custom" }).

Citry.events.applyActions

Apply a valid result envelope's actions array to the current page:

await Citry.events.applyActions(result.actions);

The method validates the array, applies its actions in order, and returns a Promise. It is primarily useful for custom transports, integration tests, and hosts that intercept Citry event responses.

The Events guides cover State, template bindings, returned actions, and direct HTTP routes. Alpine runtime covers plugins, CSP, graph markers, and deployment.