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.

1

Component Creation

Component is instantiated via ComponentFactory

2

prepare_once()

One-time initialization (called only before first prepare)

3

prepare()

Fetch data from stores, populate template elements

4

handle_action()

Handle user interactions (if action triggered)

5

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:

Vala
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"}'

Next Steps