Examples demonstrating how to integrate Lit web components with Datastar using the built-in data-attr plugin.
You don't need a custom plugin to pass complex objects to Lit components!
Datastar's built-in data-attr works perfectly:
<flow-diagram
data-attr:nodes="$flow.nodes"
data-attr:edges="$flow.edges"
data-attr:config="$flow.config"
></flow-diagram>When you pass an object or array to data-attr, it internally calls JSON.stringify():
// From Datastar's attr.ts plugin
} else {
el.setAttribute(key, JSON.stringify(val))
}Because JSON.stringify() runs inside Datastar's reactive effect, it reads all nested properties. This means Datastar tracks dependencies on every nested value:
// When this runs inside the effect:
JSON.stringify($flow.nodes)
// It accesses: $flow.nodes[0].id, $flow.nodes[0].label,
// $flow.nodes[0].x, $flow.nodes[0].y, $flow.nodes[0].color, etc.
// All become tracked dependencies!Lit's @property() decorator with type: Array or type: Object automatically parses JSON strings from attributes:
@property({ type: Array }) nodes: FlowNode[] = []
@property({ type: Object }) config: FlowConfig = { ... }This repo includes three working examples:
- Demonstrates arrays of objects (
nodes,edges) - Nested object mutations (
$flow.nodes[0].color = 'red') - Array mutations (
$flow.nodes.push(...))
- Three.js integration
- Config object with multiple properties
- Real-time property updates
- ECharts integration
- Array data binding
- Config object binding
import { LitElement, html } from 'lit'
import { customElement, property } from 'lit/decorators.js'
interface MyConfig {
color: string
size: number
}
@customElement('my-component')
export class MyComponent extends LitElement {
// Use type: Object or type: Array for automatic JSON parsing
@property({ type: Object }) config: MyConfig = { color: 'blue', size: 10 }
@property({ type: Array }) items: string[] = []
render() {
return html`
<div style="color: ${this.config.color}">
${this.items.map(item => html`<p>${item}</p>`)}
</div>
`
}
}<div data-signals='{
"config": { "color": "red", "size": 20 },
"items": ["Apple", "Banana", "Cherry"]
}'>
<my-component
data-attr:config="$config"
data-attr:items="$items"
></my-component>
<!-- Nested changes work! -->
<button data-on:click="$config.color = 'green'">Change Color</button>
<!-- Array mutations work! -->
<button data-on:click="$items.push('Date')">Add Item</button>
</div>This approach works great for JSON-serializable data. If you need to pass:
- Functions
- Date objects
- Map/Set
- Circular references
- Class instances with methods
You'll need a different approach (custom plugin or direct property setting via data-on).
# Install dependencies
pnpm install
# Build and run dev server
pnpm dev
# Build only
pnpm buildMIT