Skip to content

API At a Glance

SymbolPurposeExecution modeCommon gotcha
define()Register a custom element with reactive setupSyncTag must contain a hyphen; call before first use
htmlTagged template literal that returns HTMLResultSyncExpressions must be signals, functions, or primitives
effect()Component-scoped reactive side effectSyncRuns inside component lifetime — auto-cleaned up
onMounted()DOM-ready callback after template mountsSyncDOM queries inside setup() run before mount
onCleanup()Register teardown for component disconnectSyncMust be called synchronously during setup
onElement()Run callback when a ref resolves to an elementSyncRe-runs when the element reference changes
prop.*Typed prop helpers (string, bool, number, …)SyncProp values are signals — read .value
provide/injectContext API for parent-to-descendant sharingSyncCall provide() during setup, not in onMounted
ref()Reactive reference to a DOM elementSyncValue is null until after first mount
createContext()Create a typed injection keySyncContext is scoped to the component tree

Package Entry Points

ImportPurpose
@vielzeug/craftitCore authoring/runtime API plus stateit re-exports
@vielzeug/craftit/controlsHeadless interaction controls
@vielzeug/craftit/observersResize, intersection, and media-query observers
@vielzeug/craftit/testingDOM-oriented test helpers

Core Component API

define(tag, definition)

ts
define<Props, Events>(tag: string, definition: ComponentDefinition<Props, Events>): string;

setup() receives:

ts
type SetupContextBag<Events> = {
  emit: EmitFn<Events>;
  host: ComponentHost;
  slots: ComponentSlots;
};

setup() returns a template function:

ts
type ComponentTemplate = () => HTMLResult;
ts
define('observed-box', {
  setup() {
    const boxRef = ref<HTMLDivElement>();
    const size = signal({ width: 0, height: 0 });

    onMounted(() => {
      if (!boxRef.value) return;
      const observer = resizeObserver(boxRef.value);

      effect(() => {
        size.value = observer.value || { width: 0, height: 0 };
      });
    });

    return () => html`<div ref=${boxRef}>${size.value.width}x${size.value.height}</div>`;
  },
});

Runtime Helpers

effect(fn, options?)

Component-aware wrapper around stateit's effect(). Cleanups registered inside the effect are tied to the current Craftit runtime.

onMounted(fn)

ts
onMounted(fn: () => void | CleanupFn): void;

Registers work that runs after the component template is mounted. Multiple calls are supported and run in registration order.

handle(target, event, listener, options?)

Adds an event listener and automatically removes it on cleanup when called inside component setup or another Craftit runtime scope.

If you call it outside a runtime scope, Craftit returns a manual cleanup function and emits a one-time warning in development.

onCleanup(fn)

ts
onCleanup(fn: CleanupFn): void;

Registers a cleanup function to be called when the component disconnects. Must be called synchronously during component setup() or inside scope.run(). Cleanup functions run in LIFO order.

ts
define('my-el', {
  setup() {
    const ws = new WebSocket('wss://...');
    onCleanup(() => ws.close()); // runs on disconnect

    return () => html`...`;
  },
});

scope()

ts
scope(): Scope;

Re-exported from @vielzeug/stateit. Creates an isolated cleanup context. Use inside component setup() to manage sub-scoped teardown independently from the component lifetime.

ts
define('my-el', {
  setup() {
    const sub = scope();
    onCleanup(() => sub.dispose()); // tie sub-scope to component lifetime

    onMounted(() => {
      sub.run(() => {
        const id = setInterval(tick, 200);
        onCleanup(() => clearInterval(id));
      });
    });

    return () => html`...`;
  },
});

Registers teardown for the current component/runtime scope.

onElement(ref, callback, options?)

Runs callback when a ref() resolves to an element and re-runs when that element changes.

Props API

Craftit does not expose defineProps(). Define props directly in props using prop.* helpers or raw PropDef objects.

Shared prop bundles should type against PropsDef<T>.

HelperSignatureNotes
prop.string(defaultValue)PropDef<string>Reflects by default
prop.bool(defaultValue?)PropDef<boolean>Empty string and 'true' parse as true
prop.number(defaultValue?)PropDef<number>Uses Number(...) parsing
prop.oneOf(allowed, defaultValue)PropDef<T>Restricts to provided string union

When you need custom parsing or reflect: false, use a raw PropDef object:

ts
props: {
  label: { default: 'Save', reflect: false },
  count: { default: 0, parse: (value) => (value == null ? 0 : Number(value)) },
}

Template and Directives

  • html(strings, ...values): HTMLResult
  • css
  • each(source, options)
  • classMap(record)
  • styleMap(record)
  • guard(deps, render)
  • when(condition, truthy, falsy?)
  • live(signal)
  • until(promise, placeholder?, onError?)
  • raw(value)

live(signal) is intended for form-control bindings such as :value or :checked when you want to avoid clobbering user edits with stale app-state writes.

Host and Slots

Host API from setup context:

  • host.el
  • host.attr(name, value)
  • host.class(record)
  • host.style(record)
  • host.prop(name, descriptor)
  • host.on(event, listener)
  • host.bind({ attr, class, style, prop, on })

Other helpers:

  • syncAria(target, config)
  • slots.has(name?)
  • slots.elements(name?)

Utilities

  • ref<T>()
  • refs<T>()
  • createId(prefix?)

Context API

  • createContext<T>(description?)
  • provide(key, value)
  • inject(key)
  • inject(key, fallback)
  • injectStrict(key)

Form-Associated API

  • defineField(options): FormFieldHandle
  • type FormFieldOptions<T>
  • type FormFieldHandle

Controls APIs

Import from @vielzeug/craftit/controls.

  • createTextField(options)
  • createChoiceField(options)
  • createCheckableFieldControl(options)
  • createListControl(options)
  • createPressControl(options)
  • createSwipeControl(options)
  • createOverlayControl(options)
  • createPopupListControl(options)
  • createSliderControl(options)
  • createSpinnerControl(options)

Observer APIs

Import from @vielzeug/craftit/observers.

  • resizeObserver(element)
  • intersectionObserver(element, options?)
  • mediaObserver(query)

Testing APIs

Import from @vielzeug/craftit/testing.

  • install(afterEachHook)
  • cleanup()
  • flush()
  • mount(...)
  • within(element)
  • mock(tagName, template?)
  • fire.*
  • user.*
  • waitFor(callback, options?)
  • waitForEvent(element, name, timeout?)

Stateit Re-exports

Craftit re-exports these from @vielzeug/stateit:

  • signal, computed, effect, watch, batch, untrack
  • isSignal, type Signal, type ReadonlySignal, type WatchOptions