A Svelte 5 component library that renders interactive UIs from declarative JSON specifications. Designed for AI-generated interfaces — an LLM produces a JSON spec, and Ripple renders it as a fully reactive UI.
{state.user.name} syntax with comparisons, ternary, and logical operatorsbun add @ripple-ui/svelte
Requires Svelte 5 (^5.0.0).
<script lang="ts">
import { Ripple } from '@ripple-ui/svelte';
const spec = {
version: '1.0',
state: { count: 0 },
ui: {
type: 'flex',
props: { direction: 'column', gap: 4 },
children: [
{ type: 'heading', props: { text: 'Counter', level: 2 } },
{ type: 'text', props: { text: 'Count: {state.count}' } },
{
type: 'button',
props: { label: '+1' },
on_click: { action: 'set', target: 'count', value: '{state.count}' }
}
]
}
};
</script>
<Ripple {spec} />
Explicit widget tree with props, events, and control flow:
{
"version": "1.0",
"state": { "query": "" },
"ui": {
"type": "flex",
"props": { "direction": "column", "gap": 3 },
"children": [
{ "type": "input", "props": { "placeholder": "Search..." }, "bind": "{state.query}" },
{
"type": "if",
"condition": "{state.query != ''}",
"children": [{ "type": "text", "props": { "text": "Searching: {state.query}" } }]
}
]
}
}
Declare what the UI should do, and Ripple picks the layout:
{
"version": "2.0",
"intent": "browse",
"title": "Products",
"data": {
"items": [
{ "id": "1", "name": "Widget", "image": "/img/widget.jpg", "price": "$9.99" }
]
},
"fields": { "title": "name", "image": "image", "subtitle": "price" },
"selection": "single"
}
| Category | Widgets |
|---|---|
| Layout | container, flex, grid, card, tabs, dashboard, dashboard-slot |
| Display | text, heading, image, badge, progress, avatar, metric, feed |
| Input | button, input, select, checkbox, switch |
| Data | table, chart |
| Control | if, each |
| Composite | terminal |
Reactive bindings using {expression} syntax:
{state.user.name} — State path
{item.price} — Loop variable
{state.count > 0} — Comparison
{state.active ? 'On' : 'Off'} — Ternary
{state.a && state.b} — Logical AND
{!state.loading} — Negation
Declarative handlers with 8 action types:
{
"on_click": [
{ "action": "set", "target": "loading", "value": true },
{ "action": "api", "url": "/api/save", "method": "POST" },
{ "action": "toast", "message": "Saved!", "variant": "success" }
]
}
| Action | Behavior |
|---|---|
set |
Update state |
open |
Set state to true (dialog shorthand) |
api |
HTTP request (emitted to parent) |
navigate |
URL navigation (emitted to parent) |
toast |
Show notification (emitted to parent) |
emit |
Custom event (emitted to parent) |
pin / unpin |
Sidebar persistence (emitted to parent) |
import { registerWidget } from '@ripple-ui/svelte';
import MyWidget from './MyWidget.svelte';
registerWidget('my-widget', MyWidget);
bun install # Install dependencies
bun run dev # Dev server with playground
bun run build # Build library to dist/
bun run check # Type-check
bun run test # Run tests
Full documentation in docs/:
MIT