Web components offer native UI primitives. However, they often introduce complex lifecycles, Shadow DOM issues, and verbose code.
SXO + Reactive Component solves this. It combines server-side rendering with vanilla JSX and a signal-based reactive system. You get declarative authoring, instant page loads, and progressive enhancement without hydration.
Server-First, Reactivity-Second
SXO inverts the traditional model:
- Server: Renders semantic HTML wrapped in custom elements (e.g.,
<product-card>). - Client: A lightweight reactive runtime (~4.8KB) binds state and behavior to the existing DOM.
There is no hydration pass. The HTML is the source of truth.
Core Concepts
Reactive Components use $-prefixed attributes to link HTML to behavior.
-
$state="key": Initializes state from text content. Updates reflect automatically. -
$bind-*="key": Two-way binds attributes (likevalueorchecked) to state. -
$on*="method": Binds events to client-side handlers. -
define(tag, setup): Defines client logic, state, effects ($effect), and events ($on).
Product Card
Lets create a product card with a quantity selector and a "Favorite" toggle.
1. Server Component (JSX)
Renders accessible HTML on the server. No client-side logic is included here.
// src/components/product-card.jsx
export function ProductCard({ title, price, image }) {
return (
<product-card class="card">
<img src={image} alt={title} />
<h3>{title}</h3>
<p>${price}</p>
<div class="controls">
<button type="button" $onclick="decrement" aria-label="Decrease">-</button>
<input type="number" $bind-value="qty" value="1" min="1" aria-label="Qty" />
<button type="button" $onclick="increment" aria-label="Increase">+</button>
</div>
<button
type="button"
$onclick="toggleFavorite"
$bind-attr="favoriteAttrs"
aria-pressed="false"
class="btn-icon"
>
♥
</button>
</product-card>
);
}
2. Client Enhancer
Attaches behavior to the server-rendered markup.
// src/components/product-card.client.js
import { define } from "@qery/reactive-component";
define("product-card", ({ $state, $on, $compute }) => {
// 1. Initialize State
$state.qty = 1;
$state.isFavorite = false;
// 2. Compute aria-pressed attribute declaratively
// and falsy values remove them
$compute("favoriteAttrs", ["isFavorite"], (isFavorite) => ({
"aria-pressed": isFavorite ? "true" : "false",
}));
// 3. Define Actions
$on.increment = () => $state.qty++;
$on.decrement = () => {
if ($state.qty > 1) $state.qty--;
};
$on.toggleFavorite = () => {
$state.isFavorite = !$state.isFavorite;
};
});
3. Page Composition
Compose the component in a server route.
// src/pages/products/index.jsx
import { ProductCard } from "../../components/product-card.jsx";
export default function ShopPage({ products }) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<title>Shop</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<main>
${products.map(p => ProductCard(p)).join('')}
</main>
</body>
</html>
`;
}
Key Advantages
- Instant Interaction: The UI is visible immediately. No loading spinners or layout shifts.
- Simple State: Signal-based state management without complex providers.
- Zero-Config: Automatic bundling of per-route client entries.
Top comments (0)