Theme
Version
GitHub PyPI Discord
On this page

Component

The base class every component subclasses.

View source

Component class

Base class for all Citry components.

A component is a reusable unit of UI defined by:

  • A template (Citry template syntax)
  • Optional typed inputs (via inner Kwargs, Slots classes)
  • A data method that maps inputs to template variables

Subclass this to define your own components. At minimum, set template (inline string) or template_file (path to file).

View source

class_id attribute

class_id: str

Stable import-derived identity shared by reloads of this component path.

The read-only value is suitable for routes and cross-process logical identity. Combine it with Citry.engine_id and definition_id when retained metadata must match one exact live class generation in the current process.

View source

definition_id attribute

definition_id: str

Opaque process-lifetime identity of this exact component class object.

The read-only value exists before class-created extension hooks run. An alias or re-registration preserves it, while defining a replacement class creates a different value even when class_id remains the same.

View source

citry attribute

citry: Citry

The Citry instance that owns this component class.

Defaults to the module-level default instance. Set this inside the class body to assign a component to a specific instance. The binding cannot be changed or deleted after the class is defined. A subclass of a concrete component uses the same owner; define a fresh component tree when another engine needs its own copy.

View source

transparent attribute

transparent: bool

Whether this component's output joins the surrounding component's serialization frame.

A transparent component is structural rather than visual: its rendered output gets no data-cid-<id> marker and is not framed as a child component at serialize time. Used by built-ins like <c-provide> that only wrap content. Hooks, the render id, and dependency merging behave the same as for any component.

View source

simple attribute

simple: bool

Render a presentation template under its caller's ownership.

A simple component has no independent instance, component hooks or browser identity. Unsupported declarations and invocations raise errors. The flag inherits to subclasses, which are checked independently, and is fixed after class definition.

Use the default data method or a synchronous static template_data(kwargs, slots) method. A template can accept optional default content, but the class cannot declare its own JS, CSS, messages, instance hooks or instance configuration. The data callback still runs for each invocation; this flag does not make the component pure.

See Simple components for the full contract and Performance to compare the available options.

View source

pure attribute

pure: bool

Whether repeated equal template data may reuse settled body strings.

Set pure = True only when rendering the template is a deterministic, side-effect-free function of its template variables. The memo lives for one root render. It can reuse safe strings around a child or Slot, but the child, Slot, ordinary component instances, IDs, ownership, and i18n work still run for every occurrence. A separately declared simple component keeps its restricted instance-free contract. A subclass must declare purity again rather than inheriting the promise.

View source

name attribute

name: str | None

Override the name under which this component is registered.

By default, the class name is used (lowercased + kebab-case). Set this to register under a specific name instead::

class MyWidget(Component):
    name = "fancy-widget"
    # registered as "fancy-widget", not "mywidget" / "my-widget"
View source

template attribute

template: str | None

Inline template string (Citry template syntax).

Mutually exclusive with template_file. Read the loaded template with get_template().

View source

template_file attribute

template_file: str | None

Path to a template file. Mutually exclusive with template.

Resolved relative to the directory of the class that declares the value first, then relative to the owning component's Citry(dirs=...) entries; absolute paths are used as-is. A subclass that inherits this declaration therefore keeps the declaring class's file location. A plain mixin can declare the path while the component still supplies the owning engine.

View source

messages attribute

messages: str | None

Inline source-locale Fluent messages for this component.

Mutually exclusive with messages_file. Declare the source language with I18n.messages_locale. A registered message asset activates server source-mode translation for the complete engine catalog, even without engine i18n settings. Read the loaded source with get_messages().

View source

messages_file attribute

messages_file: str | None

Path to source-locale Fluent messages, resolved like template_file.

This has the same source-mode and I18n.messages_locale contract as messages.

View source

js attribute

js: str | None

Inline primary JS for this component. Mutually exclusive with js_file. Read the loaded content with get_js().

View source

js_file attribute

js_file: str | None

Path to the component's primary JS file. Mutually exclusive with js. Resolved like template_file.

View source

css attribute

css: str | None

Inline primary CSS for this component.

Mutually exclusive with css_file. Citry adds these selectors to the page exactly as written, so they can style any matching element. Use class names specific to the component to avoid styling something else by accident. Values returned by css_data() become custom properties for one rendered use of the component. Read the loaded content with get_css().

View source

css_file attribute

css_file: str | None

Path to the component's primary CSS file. Mutually exclusive with css. Resolved like template_file.

View source

Cache attribute

Cache: type | None

Optional output-cache settings owned by the Cache extension.

Define a nested Cache class to enable caching, set its TTL and version, or return additional variation values. Citry rebuilds the declaration on CacheConfig when it creates the component class.

View source

Dependencies attribute

Dependencies: type | None

Optional secondary JavaScript and CSS assets.

Define a nested Dependencies class with js, css, extend, or local_files. Read the normalized merged result with get_dependencies(). Citry rebuilds the declaration on DependenciesConfig.

View source

I18n attribute

I18n: type | None

Optional per-component settings for the built-in i18n extension.

Define client_messages here when browser code uses a finite dynamic message name that static analysis cannot discover. The instance-level i18n value provides translation, formatting, parsing, and the explicit locale context during a render.

View source

Kwargs attribute

Kwargs: type | None

Optional typed keyword arguments.

Define as a plain class with type annotations. The metaclass combines it with parent component declarations and converts the result to a dataclass (with slots) automatically::

class Card(Component):
    class Kwargs:
        title: str
        body: str = ""
View source

Slots attribute

Slots: type | None

Optional typed slot definitions, inherited like Kwargs.

Use SlotInput for places where people can add content. A field without a default must be filled whenever the component is used. The required attribute on <c-slot> checks something different: it raises an error only if Citry renders that tag without content.

View source

State attribute

State: type | None

Optional typed values that survive between server event calls.

Define State as a plain nested class with type annotations. The Events extension combines inherited declarations and converts the result to a mutable, slotted dataclass automatically::

class Search(Component):
    class State:
        query: str = ""
        page: int = 1

State must contain only JSON-serializable values. By default, every field is readable and writable in the browser. Use _public to choose which fields the browser may read and _model to choose which public fields it may change. _storage is "signed" by default and may be set to "server". _max_bytes defaults to 8192 bytes, and _max_age accepts a datetime.timedelta or None for no expiry.

Citry starts State from same-named keyword arguments and field defaults. Define state_data(self, kwargs, slots) when the values need to be derived instead. Assign State = None on a subclass to stop inheriting its parent's State declaration.

View source

Events attribute

Events: type | None

Optional server event handlers for this component.

Define Events as a nested class. Every public method is an event handler; underscore-prefixed methods and attributes are private helpers or configuration::

class Counter(Component):
    class State:
        count: int = 0

    class Events:
        def increment(self, state):
            state.count += 1

Citry combines inherited Events declarations in component C3 order. A child method overrides a same-named parent method, while Events = None stops inherited declarations. The built-in Events extension rebuilds the effective nested class on its runtime config base.

A plain nested class works without imports. To type handler attributes such as self.state and self.request, subclass the generic Events base and parameterize it with the component's State class. See event for per-handler options.

View source

Lint attribute

Lint: type | None

Optional per-component template-lint settings.

Define a nested Lint class with rule_unknown_template_variable and/or template_variables. Nested declarations compose through the component C3 order. Assign None to return to the Citry instance's application lint policy.

View source

TemplateData attribute

TemplateData: type | None

Optional typed template data output, inherited like Kwargs.

View source

JsData attribute

JsData: type | None

Optional typed schema for the js_data() output. Like TemplateData, it inherits through component C3 and a plain annotated class converts to a dataclass.

View source

CssData attribute

CssData: type | None

Optional typed schema for the css_data() output. Like TemplateData, it inherits through component C3 and a plain annotated class converts to a dataclass.

View source

id attribute

id: str

Unique render ID for this component instance.

A fresh ID is minted every time a CitryElement is rendered, so the same CitryElement rendered twice produces two distinct IDs.

View source

kwargs attribute

kwargs: Any

The resolved keyword arguments.

If the component defines a Kwargs dataclass, this is an instance of that class. Otherwise, a plain dict.

View source

raw_kwargs attribute

raw_kwargs: dict[str, Any]

The keyword arguments as a plain dict, even if a Kwargs dataclass is defined. Useful when you need dict access regardless of typing.

View source

slots attribute

slots: Any

The resolved slot fills, with every value normalized to a Slot.

If the component defines a Slots dataclass, this is an instance of that class. Otherwise, a plain dict.

View source

raw_slots attribute

raw_slots: dict[str, Slot]

The slot fills as a plain dict of Slot values, even if a Slots dataclass is defined. Useful when you need dict access regardless of typing.

View source

cache attribute

The Cache extension settings bound to this rendered component.

View source

dependencies attribute

dependencies: DependenciesConfig

The Dependencies extension settings bound to this rendered component.

View source

events attribute

events: EventsConfig[Any]

The Events extension settings and event URL helper for this component.

View source

i18n attribute

i18n: I18nConfig

Translation, formatting, parsing, and locale access for this component.

View source

parent attribute

parent: Component | None

The component that wrote this one into its template. None for a root component, and for one rendered standalone (e.g. an element handed into an expression as {{ element }}).

The link follows authorship, not slot placement: a component written inside a <c-fill> keeps the fill's author as its parent, no matter whose slot the content lands in. (This differs from Vue, whose $parent points at the slot host.) To ask "what am I rendered inside, slots included", use provide/inject, which travels the render path and crosses slot boundaries.

View source

root attribute

root: Component

Return the component at the top of the authorship parent chain.

For root components, self.root is self. The root case is computed instead of stored, so preserving that public identity does not create a root-to-itself reference cycle.

View source

template_data function

template_data(kwargs: Any, slots: Any) -> dict[str, Any] | None

Return the template variables.

By default this returns kwargs, so a component's inputs are usable in its template without an override: a Kwargs field named title is available to the template as {{ title }}. Override this to map the inputs to a different set of variables. The returned value may be a dict, a NamedTuple, or the typed TemplateData instance, and a declared TemplateData validates and normalizes it either way. Schema defaults and coercions are materialized in the mapping that the template's expressions see.

An input whose outer value was marked with Const reaches kwargs as an ordinary Python value; exact builtin containers under that marker are cleaned recursively. Ordinary containers are not searched for manually nested markers. The base method keeps known const inputs in renderer metadata; validating or transforming schemas may discard that metadata. An override retains an input promise when its final same-name output is the recorded input object; renamed or replaced stable outputs need an explicit Const marker.

A returned variable wins over a template_globals entry of the same name, so an input shadows a same-named global (globals act as defaults). Unlike js_data and css_data, which stay opt-in and return None by default, template variables never cross into the browser: they only make names resolvable to the template's own expressions.

Parameters

  • kwargs Any - The keyword arguments passed to the component.
  • slots Any - The slot fills passed to the component.

Returns

dict[str, Any] | None: A mapping of template variables. Defaults to the component's

View source

js_data function

js_data(kwargs: Any, slots: Any) -> dict[str, Any] | None

Return the JS variables for this render.

Override this to expose per-render data to the component's browser behavior. The dict is serialized to strict JSON, seeded into the component's Alpine scope, and delivered to its $component callback as data when one exists. Identical JSON is transported only once, while every rendered instance receives a fresh mutable value graph. Consumed by the built-in dependencies extension.

Parameters

  • kwargs Any - The keyword arguments passed to the component.
  • slots Any - The slot fills passed to the component.

Returns

dict[str, Any] | None: A dict of JS variables, or None for no variables.

View source

css_data function

css_data(kwargs: Any, slots: Any) -> dict[str, Any] | None

Return the CSS variables for this render.

Override this to expose per-render values to the component's CSS (Component.css) as CSS custom properties: a returned {"row-color": "red"} is usable in the CSS as var(--row-color), scoped to this component's elements. Identical data across renders shares one generated stylesheet. Consumed by the built-in dependencies extension.

Keys are custom-property name suffixes, without the leading --. Values must be strings, finite numbers, or None. Citry escapes quoted strings and rejects names or raw values that could escape the generated declaration. It checks structural containment, while the browser remains responsible for full CSS value grammar and whether a value is valid for the property that consumes it.

Parameters

  • kwargs Any - The keyword arguments passed to the component.
  • slots Any - The slot fills passed to the component.

Returns

dict[str, Any] | None: A dict of CSS variables, or None for no variables.

View source

on_dependencies function

on_dependencies(scripts: list[Dependency], styles: list[Dependency]) -> tuple[list[Dependency], list[Dependency]] | None

Hook to adjust this component's JS/CSS tags before they enter the page.

Called at serialize time, once per rendered instance of this component, with the Script/Style entries this component contributes (its Dependencies entries and its own Component.js/css). Return a (scripts, styles) pair to replace the lists, mutate them in place, or return None (the default) to keep them. Removing the component's own script entries can break the component's behavior in the browser; this hook is for adding attributes, reordering, or dropping entries you know are provided elsewhere.

To adjust the page-wide lists instead (every component's tags, after de-duplication), implement an extension with an on_dependencies method (see citry.ext.dependencies.OnDependenciesContext).

View source

on_render function

on_render() -> RenderReplacement | OnRenderGenerator | None

Hook to replace or post-process this component's rendered output.

Called when this component is rendered without a successful component cache hit, after template_data and just before the template renders. A cache hit reuses the completed output and skips data methods, slots, the template, and this hook. Return None (the default) to render the template as usual. Return content to use it as the component's whole output instead; the template is then not rendered at all. Accepted content:

  • a str, used as-is (NOT autoescaped: it is this component's own output, the same trust as its template; never concatenate untrusted input into it)
  • a composed element (OtherComponent(title="hi")), rendered in this component's place
  • an already-rendered CitryRender, inlined
  • a Slot, invoked with no data
  • a ComponentLike, resolved against this component's Citry instance

Because None means "no replacement", return "" to output literally nothing.

Everything the hook needs is on self: kwargs, slots, parent, inject(). To pass data to the template, use template_data; this hook is for replacing output. If the hook depends on ambient data while component caching is enabled, include that data in the cache variation inputs.

For example, render a placeholder instead of the template when there is no data::

class MyTable(Component):
    template = "<table>...</table>"

    def on_render(self):
        if not self.raw_kwargs.get("rows"):
            return "<p>No data</p>"
        return None

Generator form. Include a yield to also see the component's finished output, children included, and react to it - for example to catch a failing child (this is how error boundaries work)::

class Guarded(Component):
    template = "..."

    def on_render(self):
        # BEFORE: runs just before the template renders.
        result, error = yield

        # AFTER: result is the completed CitryRender, or None
        # if rendering failed (then error is the exception).
        if error is not None:
            return "<p>Something went wrong</p>"
        return None

The protocol:

  • A bare yield (or yield None) on the first yield means "render my template as usual"; yielding content means "use this as my output instead" (same accepted values as above).
  • The yield receives (result, error) once that output has fully settled: result is the live CitryRender (not a string; do not serialize it here unless you are replacing the output with the serialized form), or None when rendering failed, with error set. Exactly one of the two is set.
  • You can yield any number of times; each yield <content> replaces the output, renders it, and receives the new (result, error). A bare yield after the first answers immediately with the current result unchanged.
  • End with return <content> to set the final output, raise to make that the component's error, or plain return to keep the current result (an unhandled error keeps bubbling).
View source

provide function

provide(key: str, value: Any = MISSING, **data: Any = {}) -> None

Make one value available to this component's descendants.

Any component rendered below this one (including components inside slot content rendered below it) can read the data with self.inject(key). The data does NOT enter the template variables; descendants opt in explicitly.

Pass a direct positional value when the caller already owns the value object. Or pass keyword fields and Citry will freeze them into an immutable payload whose fields are read as attributes::

class Page(Component):
    template = '<c-user-card />'

    def template_data(self, kwargs, slots):
        self.provide("user_data", user=kwargs["user"])
        return {}

class UserCard(Component):
    template = '<div>{{ name }}</div>'

    def template_data(self, kwargs, slots):
        return {"name": self.inject("user_data").user}

class LocaleRoot(Component):
    def template_data(self, kwargs, slots):
        self.provide("citry_i18n", kwargs.locale_context)
        return {}

In templates, the same thing is written with the <c-provide> built-in component: <c-provide key="user_data" c-user="user">.

Parameters

  • key str - Name the data is provided under (a non-empty identifier). Positional-only, so a data field named key is allowed.
  • value Any - One direct value. It is passed through unchanged.
  • **data Any - Fields Citry freezes into one immutable payload. A call cannot pass both a direct value and keyword fields.
View source

inject function

inject(key: str, default: Any = MISSING) -> Any

Read data a component above this one provided under key.

The data must have been provided by a component on the render path above this one (via Component.provide or the <c-provide> built-in); the nearest provider wins when the same key is provided twice. A component's own provide calls are visible to its descendants only, never to its own inject.

A direct value is returned unchanged. Keyword fields passed to provide() return an immutable payload with those fields as attributes: self.inject("user_data").user. Injection works during template_data and keeps working after the render for as long as the component instance is kept.

Parameters

  • key str - The name the data was provided under.
  • default Any - Returned when nothing was provided under key. An explicit None works. Without a default, a missing key raises KeyError.
View source

unprovide function

unprovide(key: str) -> None

Hide an inherited provide from this component's descendants.

The component may still inject the inherited value itself. Components rendered below it observe the key as missing unless a nearer component provides a new value under the same key. Call this from template_data when content below a component boundary must establish a fresh context before using a compound child.

Parameters

  • key str - The provide key to hide below this component.
View source

ancestors attribute

ancestors: Iterator[Component]

All ancestor components, nearest first: the parent, then the parent's parent, up to and including the root. Empty for a root component.

Useful to check where a component sits, e.g.::

is_themed = any(isinstance(c, Theme) for c in self.ancestors)

The chain follows who wrote the component, the same as parent: a component written inside a <c-fill> has the fill's author as its parent, not the component whose slot rendered it. So the check above holds when Theme's own template renders this component; for "am I rendered inside a Theme, slots included", have Theme provide a value and inject it here, which travels the render path and crosses slot boundaries.

View source

get_template function

get_template() -> CitryTemplate | None

The loaded template (a CitryTemplate), or None for a template-less component. Resolved from template / template_file once per class; on_template_loaded applied.

View source

get_js function

get_js() -> str | None

The loaded primary JS content, or None. Resolved from js / js_file once per class; on_js_loaded applied.

View source

get_messages function

get_messages() -> str | None

Return the loaded messages / messages_file source, or None.

View source

get_css function

get_css() -> str | None

The loaded primary CSS content, or None. Resolved from css / css_file once per class; on_css_loaded applied.

View source

get_dependencies function

get_dependencies() -> CitryDependencies

The merged secondary assets from this component's (and, per Dependencies.extend, its bases') nested Dependencies class. Owned by the built-in dependencies extension.

View source

reset_template function

reset_template() -> None

Clear this class's loaded template (and its compiled form and cached Const optimization results), so the next render re-reads it. Subclasses that inherit this template cache their own copies; reset them too (Citry.get_components_for_file lists every class using a given file).

View source

reset_files function

reset_files() -> None

Clear this class's loaded messages/JS/CSS (and, via the on_files_reset hook, extension state such as the merged Dependencies), so the next access re-reads them.

Citry version: 0.5.1