Components
Components are lightweight Web Components with typed props, optional state, and a render function.
ts
import { component, html, safeHtml } from '@bquery/bquery/component';
component('user-card', {
props: {
username: { type: String, required: true },
avatar: { type: String, default: '/default-avatar.png' },
active: { type: Boolean, default: false },
},
state: { clicks: 0 },
styles: `
.card { display: grid; gap: 0.5rem; padding: 1rem; }
.active { border: 1px solid #4f46e5; }
`,
connected() {
console.log('mounted');
},
updated() {
console.log('updated');
},
render({ props, state }) {
return html`
<div class="card ${props.active ? 'active' : ''}">
<img src="${props.avatar}" alt="${props.username}" />
<strong>${safeHtml`${props.username}`}</strong>
<div>Clicks: ${state.clicks}</div>
</div>
`;
},
});Props
Props are defined with a type and optional required/default/validator.
ts
props: {
count: { type: Number, default: 0 },
enabled: { type: Boolean, default: true },
meta: { type: Object, default: {} },
age: {
type: Number,
default: 0,
validator: (v) => v >= 0 && v <= 150
},
}Prop validation
Add a validator function to validate prop values:
ts
props: {
email: {
type: String,
required: true,
validator: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
},
}If validation fails, the component throws an error (caught by onError if defined).
Prop coercion
String→ raw stringNumber→Number(value)(fallback to raw string onNaN)Boolean→'true' | '' | '1'=>true,'false' | '0'=>falseObject/Array→JSON.parse(fallback to raw string)- Custom function/constructor → called or constructed
Missing required props without a default throw an error at runtime.
State
State is a simple internal object that can be updated via setState.
ts
const el = document.querySelector('user-card') as HTMLElement & {
setState: (key: string, value: unknown) => void;
};
el.setState('clicks', 1);Lifecycle hooks
beforeMount()– runs before the element renders (can modify initial state)connected()– runs when the element mountsbeforeUpdate(props)– runs before re-render; returnfalseto prevent updateupdated()– runs after re-render on prop changesdisconnected()– runs on teardownonError(error)– handles errors during lifecycle/render
ts
component('my-element', {
props: { count: { type: Number, default: 0 } },
beforeMount() {
console.log('About to mount');
},
connected() {
console.log('Mounted');
},
beforeUpdate(props) {
// Prevent update if count is negative
if (props.count < 0) return false;
},
updated() {
console.log('Updated');
},
disconnected() {
console.log('Disconnected');
},
onError(error) {
console.error('Error:', error);
},
render({ props }) {
return html`<div>Count: ${props.count}</div>`;
},
});Rendering helpers
html– template literal helper for building HTML stringssafeHtml– escapes interpolated values for safety
Emitting events
The render function receives emit for custom events:
ts
render({ emit }) {
emit('change', { value: 1 });
return html`<div>...</div>`;
}