Components Overview
What are Spry Components?
Spry components are the building blocks of your web application. They combine HTML templates with behavior logic in a single, cohesive Vala class. Each component is self-contained with its own markup, state management, and action handling.
Unlike traditional web frameworks where templates and logic are separated, Spry components embrace a component-based architecture where everything related to a UI element lives in one place.
Component Lifecycle
Understanding the component lifecycle is crucial for building effective Spry applications. The template DOM is fresh on every request, meaning state is not persisted between requests.
Component Creation
Component is instantiated via ComponentFactory
prepare_once()
One-time initialization (called only before first prepare)
prepare()
Fetch data from stores, populate template elements
handle_action()
Handle user interactions (if action triggered)
Serialization
Template is rendered to HTML and sent to client
Key Methods
Every component inherits from the base Component class and can
override these key methods:
| Method | Description |
|---|---|
markup |
Property returning the HTML template string with Spry attributes |
prepare() |
Called before serialization. Fetch data and populate template here. |
prepare_once() |
Called only once before the first prepare(). For one-time initialization. |
handle_action() |
Called when HTMX triggers an action. Modify state here. |
continuation() |
For real-time updates via Server-Sent Events (SSE). |
Basic Component Example
Here's a simple counter component demonstrating the core concepts:
using Spry;
public class CounterComponent : Component {
private CounterStore store = inject<CounterStore>();
public override string markup { get {
return """
<div sid="counter" class="counter">
<span sid="count"></span>
<button sid="inc" spry-action=":Increment"
spry-target="counter">+</button>
<button sid="dec" spry-action=":Decrement"
spry-target="counter">-</button>
</div>
""";
}}
public override void prepare() throws Error {
this["count"].text_content = store.count.to_string();
}
public async override void handle_action(string action) throws Error {
switch (action) {
case "Increment":
store.count++;
break;
case "Decrement":
store.count--;
break;
}
// prepare() is called automatically after handle_action()
}
}
Important: Template State is Not Persisted
⚠️ Critical Concept: The template DOM is fresh on every request.
Any state set via setters (like title, completed) is
NOT available in handle_action().
Always use prepare() to fetch data from stores and set up the template.
To pass data to actions, use one of these approaches:
Option 1: spry-context (Recommended for Properties)
Use <spry-context property="name"/> to automatically preserve
property values across requests.
- Properties are encrypted and included in action URLs automatically
- Values are restored to properties before
handle_action()is called - Example:
<spry-context property="item_id"/>
Option 2: hx-vals (Manual Query Parameters)
Use the hx-vals attribute to pass data as query parameters.
- Values are included in the request as query parameters
- Must be read manually via
http_context.request.query_params.get_any_or_default("key") - Example:
hx-vals='{"id": "123"}'