Quickstart¶
This walkthrough builds a stateful counter component from scratch.
1. Create a component class¶
# myapp/components.py
from shard import Component, Prop, action
class Counter(Component):
initial = Prop(int, default=0)
step = Prop(int, default=1)
template_name = "components/counter.html"
def get_initial_state(self):
return {"count": self.initial}
@action
def increment(self, state):
state["count"] += self.step
return state
@action
def decrement(self, state):
state["count"] -= self.step
return state
Shrd auto-discovers components from <app>.components when the app is in INSTALLED_APPS.
2. Create the template¶
{# templates/components/counter.html #}
<div {% shard_root component %} class="counter">
<p>Count: {{ state.count }}</p>
<button type="button" {% shard_htmx component "increment" %}>+</button>
<button type="button" {% shard_htmx component "decrement" %}>-</button>
</div>
3. Add scoped styles¶
/* templates/components/counter.css */
.counter {
display: flex;
gap: 0.5rem;
align-items: center;
}
Place counter.css beside counter.html. Shrd discovers it automatically and scopes selectors to [data-shard-scope="counter"].
4. Use it in a page¶
5. Run the server¶
Click the buttons. HTMX posts to Shrd's action endpoint, the server runs your @action method, persists updated state, and returns a re-rendered HTML fragment.
What happened?¶
{% component %}created aCounterinstance with propsinitial=5andstep=2- State was initialized via
get_initial_state()→{"count": 5} - Props, state, and slots were persisted to cache under a unique instance ID
- HTMX
hx-posthit/shard/action/<id>/increment/ - Shrd loaded state, called
increment, saved new state, re-rendered the template
That's the full loop. Everything else in Shrd builds on these primitives.