Component
The base class every component subclasses.
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,Slotsclasses) - 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).
class_id attribute
class_id: strStable 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.
definition_id attribute
definition_id: strOpaque 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.
citry attribute
citry: CitryThe 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.
transparent attribute
transparent: boolWhether 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.
simple attribute
simple: boolRender 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.
pure attribute
pure: boolWhether 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.
name attribute
name: str | NoneOverride 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"
template attribute
template: str | NoneInline template string (Citry template syntax).
Mutually exclusive with template_file. Read the loaded template with get_template().
template_file attribute
template_file: str | NonePath 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.
messages attribute
messages: str | NoneInline 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().
messages_file attribute
messages_file: str | NonePath to source-locale Fluent messages, resolved like template_file.
This has the same source-mode and I18n.messages_locale contract as messages.
js attribute
js: str | NoneInline primary JS for this component. Mutually exclusive with js_file. Read the loaded content with get_js().
js_file attribute
js_file: str | NonePath to the component's primary JS file. Mutually exclusive with js. Resolved like template_file.
css attribute
css: str | NoneInline 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().
css_file attribute
css_file: str | NonePath to the component's primary CSS file. Mutually exclusive with css. Resolved like template_file.
Cache attribute
Cache: type | NoneOptional 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.
Dependencies attribute
Dependencies: type | NoneOptional 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.
I18n attribute
I18n: type | NoneOptional 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.
Kwargs attribute
Kwargs: type | NoneOptional 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 = ""
Slots attribute
Slots: type | NoneOptional 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.
State attribute
State: type | NoneOptional 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.
Events attribute
Events: type | NoneOptional 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.
Lint attribute
Lint: type | NoneOptional 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.
TemplateData attribute
TemplateData: type | NoneOptional typed template data output, inherited like Kwargs.
JsData attribute
JsData: type | NoneOptional typed schema for the js_data() output. Like TemplateData, it inherits through component C3 and a plain annotated class converts to a dataclass.
CssData attribute
CssData: type | NoneOptional typed schema for the css_data() output. Like TemplateData, it inherits through component C3 and a plain annotated class converts to a dataclass.
id attribute
id: strUnique 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.
kwargs attribute
kwargs: AnyThe resolved keyword arguments.
If the component defines a Kwargs dataclass, this is an instance of that class. Otherwise, a plain dict.
raw_kwargs attribute
The keyword arguments as a plain dict, even if a Kwargs dataclass is defined. Useful when you need dict access regardless of typing.
slots attribute
slots: AnyThe 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.
raw_slots attribute
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.
cache attribute
cache: CacheConfigThe Cache extension settings bound to this rendered component.
dependencies attribute
dependencies: DependenciesConfigThe Dependencies extension settings bound to this rendered component.
events attribute
events: EventsConfig[Any]The Events extension settings and event URL helper for this component.
i18n attribute
i18n: I18nConfigTranslation, formatting, parsing, and locale access for this component.
parent attribute
parent: Component | NoneThe 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.
root attribute
root: ComponentReturn 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.
template_data function
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
kwargsAny- The keyword arguments passed to the component.slotsAny- The slot fills passed to the component.
Returns
dict[str, Any] | None: A mapping of template variables. Defaults to the component's
js_data function
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
kwargsAny- The keyword arguments passed to the component.slotsAny- The slot fills passed to the component.
Returns
dict[str, Any] | None: A dict of JS variables, or None for no variables.
css_data function
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
kwargsAny- The keyword arguments passed to the component.slotsAny- The slot fills passed to the component.
Returns
dict[str, Any] | None: A dict of CSS variables, or None for no variables.
on_dependencies function
on_dependencies(scripts: list[Dependency], styles: list[Dependency]) -> tuple[list[Dependency], list[Dependency]] | NoneHook 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).
on_render function
on_render() -> RenderReplacement | OnRenderGenerator | NoneHook 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(oryield 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:resultis the liveCitryRender(not a string; do not serialize it here unless you are replacing the output with the serialized form), orNonewhen rendering failed, witherrorset. 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 bareyieldafter the first answers immediately with the current result unchanged. - End with
return <content>to set the final output,raiseto make that the component's error, or plainreturnto keep the current result (an unhandled error keeps bubbling).
provide function
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
keystr- Name the data is provided under (a non-empty identifier). Positional-only, so a data field namedkeyis allowed.valueAny- One direct value. It is passed through unchanged.**dataAny- Fields Citry freezes into one immutable payload. A call cannot pass both a direct value and keyword fields.
inject function
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
unprovide function
unprovide(key: str) -> NoneHide 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
keystr- The provide key to hide below this component.
ancestors attribute
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.
get_template function
get_template() -> CitryTemplate | NoneThe loaded template (a CitryTemplate), or None for a template-less component. Resolved from template / template_file once per class; on_template_loaded applied.
get_js function
get_js() -> str | NoneThe loaded primary JS content, or None. Resolved from js / js_file once per class; on_js_loaded applied.
get_messages function
get_messages() -> str | NoneReturn the loaded messages / messages_file source, or None.
get_css function
get_css() -> str | NoneThe loaded primary CSS content, or None. Resolved from css / css_file once per class; on_css_loaded applied.
get_dependencies function
get_dependencies() -> CitryDependenciesThe merged secondary assets from this component's (and, per Dependencies.extend, its bases') nested Dependencies class. Owned by the built-in dependencies extension.
reset_template function
reset_template() -> NoneClear 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).
reset_files function
reset_files() -> NoneClear 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.