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

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

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

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

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

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

The component at the top of the parent chain (the same authorship rule as parent).

For root components, self.root is self. Never None.

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 it either way; the result is what the template's expressions see.

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 JS (Component.js). The dict is serialized to JSON and delivered to the component's $component callback in the browser; identical data is sent to the browser only once, however many instances share it. 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 on every render of this component, after template_data and just before the template renders. 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)
  • 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.

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, **data: Any = {}) -> None

Make data 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.

Call this from template_data. The data is frozen into an immutable payload at this point, so what descendants inject always has exactly the fields given here, 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}

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.
  • **data Any - The provided 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.

The returned payload is immutable, with the provided fields as attributes: self.inject("user_data").user. 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_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 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.3.0