@vielzeug/craftit
v2.0.1
Published
> Functional web components with signals, typed props, and template bindings
Readme
@vielzeug/craftit
Functional web components with signals, typed props, and template bindings
Craftit provides a compact API for authoring custom elements with fine-grained reactivity. It builds on @vielzeug/stateit (re-exported from the main entry) and adds component lifecycle, templating, typed props, context, slots/emits, form-associated helpers, and observer utilities.
Installation
pnpm add @vielzeug/craftit
# npm install @vielzeug/craftit
# yarn add @vielzeug/craftitQuick Start
import { defineComponent, signal, computed, html } from '@vielzeug/craftit';
defineComponent({
setup() {
const count = signal(0);
const doubled = computed(() => count.value * 2);
return html`
<button @click=${() => count.value++}>Count: ${count}</button>
<p>Doubled: ${doubled}</p>
`;
},
tag: 'my-counter',
});Features
- ✅ Component authoring —
defineComponent({ tag, props, setup, ... }) - ✅ Signals included — all
@vielzeug/stateitexports are re-exported - ✅ Reactive templates —
htmltagged template with text/attr/prop/event/ref bindings - ✅ Lifecycle helpers —
onMount,onCleanup,onError,handle,watch,effect,fire.* - ✅ Typed component APIs —
defineComponent,prop,typed, setup-contextemitandslots - ✅ Context / DI —
createContext,provide,inject,syncContextProps - ✅ Form-associated controls —
defineFieldwithElementInternals - ✅ Observer utilities —
observeResize(main API) +observeIntersection/observeMedia(/labs) - ✅ Directive subpath —
@vielzeug/craftit/directives - ✅ Test subpath —
@vielzeug/craftit/test
Entry Points
| Entry | Purpose |
|---|---|
| @vielzeug/craftit | Main API (components + stateit re-exports) |
| @vielzeug/craftit/directives | Directive helpers like each, when, bind, match, until |
| @vielzeug/craftit/test | Mount/query/event testing utilities |
Usage Highlights
Typed props + emits
import { defineComponent, html } from '@vielzeug/craftit';
defineComponent<
{ disabled: boolean; label: string },
{ change: string }
>({
props: {
disabled: { default: false },
label: { default: 'Name' },
},
setup({ emit, props }) {
return html`
<label>${props.label}</label>
<input
:disabled=${props.disabled}
@input=${(e: Event) => emit('change', (e.target as HTMLInputElement).value)}
/>
`;
},
tag: 'name-input',
});Directives subpath
import { defineComponent, signal, html } from '@vielzeug/craftit';
import { each, when } from '@vielzeug/craftit/directives';
defineComponent({
setup() {
const todos = signal([{ id: 1, text: 'Write docs', done: false }]);
return html`
${when(
() => todos.value.length > 0,
() => html`<ul>${each(todos, (todo) => html`<li>${todo.text}</li>`, () => html``, { key: (t) => t.id })}</ul>`,
() => html`<p>No todos</p>`,
)}
`;
},
tag: 'todo-list',
});Form-associated field
import { defineComponent, defineField, signal, html } from '@vielzeug/craftit';
defineComponent({
formAssociated: true,
setup() {
const value = signal('');
const field = defineField({ value });
return html`
<input
type="email"
:value=${value}
@input=${(e: Event) => {
value.value = (e.target as HTMLInputElement).value;
field.setCustomValidity(value.value.includes('@') ? '' : 'Invalid email');
}}
/>
`;
},
tag: 'email-field',
});API Summary
| Group | Main exports |
|---|---|
| Components | defineComponent, DefineComponentOptions, DefineComponentSetupContext, BuildPropSchema |
| Runtime | onMount, onCleanup, onError, handle, aria, effect, watch, fire |
| Props | prop, typed, PropOptions, PropDef, InferPropsSignals |
| Slots / emits | setup-context slots, setup-context emit, onSlotChange, Slots, EmitFn |
| Context | createContext, provide, inject, syncContextProps, InjectionKey |
| Form | defineField, FormFieldOptions, FormFieldCallbacks, FormFieldHandle |
| Observers | observeResize, observeIntersection, observeMedia |
| Utilities | html, css, createId, createFormIds, guard, escapeHtml, toKebab |
| Re-exported from stateit | signal, computed, batch, untrack, readonly, and more |
Documentation
Full docs at vielzeug.dev/craftit
| | | |---|---| | Overview | Install and architecture overview | | Usage Guide | Practical patterns and subpath usage | | API Reference | Complete signatures and types | | Examples | End-to-end component examples |
License
MIT © Helmuth Saatkamp — Part of the Vielzeug monorepo.
