API Reference

Core functions

Micra.define()

function define<S extends StateRecord>(
  name: string,
  definition: ComponentDefinition<S>,
): void;

Registers a component definition by name for later use with data-component and Micra.start().

Example:

Micra.define("counter", {
  state: { count: 0 },
  increment() {
    this.state.count++;
  },
});

Micra.defineComponent()

function defineComponent<S extends StateRecord>(
  definition: ComponentDefinition<S>,
): ComponentDefinition<S>;

Type helper for TypeScript inference. Returns the same definition object.

Example:

const counter = Micra.defineComponent({
  state: { count: 0 },
  increment() {
    this.state.count++;
  },
});

Micra.define("counter", counter);

Micra.mount()

function mount<S extends StateRecord>(
  selector: string | HTMLElement,
  definition: ComponentDefinition<S>,
): ComponentInstance<S> | null;

Mounts a component directly onto an element.

Notes:

Example:

const instance = Micra.mount("#counter", {
  state: { count: 0 },
  increment() {
    this.state.count++;
  },
});

Micra.start()

function start(root?: Document | HTMLElement): void;

Scans a document or subtree for [data-component] and mounts registered components.

Example:

Micra.start();
Micra.start(document.getElementById("sidebar")!);

Micra.on()

function on<K extends string>(
  event: K,
  handler: (payload: EventPayload<K>) => void,
): UnsubFn;

Subscribes to a global event. Payload type is resolved through the augmentable MicraEvents interface — events not declared there fall back to unknown (backward-compatible with untyped usage).

Example:

const unsub = Micra.on("user:updated", (user) => {
  console.log(user);
});

Micra.off()

function off<K extends string>(
  event: K,
  handler: (payload: EventPayload<K>) => void,
): void;

Removes a specific global event handler.

Example:

function onOpen() {
  console.log("opened");
}

Micra.on("modal:open", onOpen);
Micra.off("modal:open", onOpen);

Micra.emit()

function emit<K extends string>(event: K, ...args: EmitArgs<K>): void;

Publishes an event on the global bus. When the event is declared in MicraEvents, the payload type and arity are enforced by the compiler — required if the declared payload doesn’t include undefined/void, optional otherwise. Unknown events accept any optional payload.

Example:

Micra.emit("modal:open");
Micra.emit("toast:show", { type: "success", message: "Saved" });

Micra.instances()

function instances(): ReadonlyMap<HTMLElement, ComponentInstance>;

Returns the live instance map keyed by root element.

Example:

for (const [el, instance] of Micra.instances()) {
  console.log(el, instance);
}

Micra.registry()

function registry(): ReadonlyMap<string, ComponentDefinition>;

Returns the registered component definition map.

Example:

console.log(Micra.registry().has("counter"));

Micra.debug()

function debug(): void;

Prints all live component instances to the browser console grouped by component name.

Example:

Micra.debug();
// [Micra] 3 live component(s)
//   counter   $el: <div>  state: { count: 5 }
//   user-list $el: <div>  state: { users: [...], loading: false }

Use this during development to inspect which components are mounted and what state they hold.

Micra.config()

function config(opts: MicraConfig): void;

Set global options. Merges into the existing config — call as many times as needed.

Example:

import DOMPurify from "dompurify";
Micra.config({ sanitize: DOMPurify.sanitize });
// now every data-html value is sanitized

Component instance

this inside component methods is a ComponentInstance<S>.

$el

readonly $el: HTMLElement

The root element.

state

state: S;

Reactive component state. Assigning a top-level property schedules a batched re-render.

refs

refs: Record<string, HTMLElement>;

Collected descendant refs from data-ref="name".

Example:

<canvas data-ref="chart"></canvas>
onCreate() {
  this.refs.chart
}

render()

render(): void

Forces an immediate synchronous render.

set()

set(path: string, value: unknown): void

Set a value by dot-path, reconstructing nested objects immutably and reassigning the top-level key so the shallow proxy fires a render. Flat paths are a normal top-level write.

this.set("user.name", "Ada"); // ≡ this.state.user = { ...this.state.user, name: "Ada" }
this.set("count", 0); // ≡ this.state.count = 0

destroy()

destroy(): void

Unmounts the instance, unsubscribes this.on() listeners, and calls onDestroy.

prop()

prop(name: string): string | undefined
prop<T>(name: string, defaultVal: T): T

Reads a data-* value from the root element and auto-casts booleans and numbers.

Example:

<section data-component="report" data-page="2" data-show-chart="true"></section>
this.prop("page", 1); // 2
this.prop("showChart", false); // true

fetch()

fetch(url: string, options?: FetchOptions): Promise<unknown>

Fetch wrapper with JSON/text parsing, query param support for GET/HEAD, JSON body support for write requests, and automatic CSRF header lookup.

Example:

await this.fetch("/api/users", { page: 2, status: "active" });
await this.fetch("/api/invite", {
  method: "POST",
  body: { email: "ana@example.com" },
});

emit()

emit<K extends string>(event: K, ...args: EmitArgs<K>): void

Shortcut to Micra.emit(). Same MicraEvents typing rules apply.

on()

on<K extends string>(event: K, handler: (payload: EventPayload<K>) => void): UnsubFn

Shortcut to Micra.on() with auto-cleanup on destroy. Payload typed via MicraEvents.

Public types

StateRecord

type StateRecord = Record<string, unknown>;

Base constraint for component state objects.

UnsubFn

type UnsubFn = () => void;

Returned by Micra.on() and this.on().

EventHandler

type EventHandler<T = unknown> = (payload: T) => void;

Generic handler type for the event bus.

MicraEvents

interface MicraEvents {}

Type-safe registry for the global event bus. Empty by default — augment it via TypeScript declaration merging to type your application’s events. Every declared key is enforced by Micra.emit, Micra.on, this.emit, and this.on. Events absent from MicraEvents fall back to unknown payload, so untyped code keeps working unchanged.

// types/micra-events.d.ts
import 'micra.js'

declare module 'micra.js' {
  interface MicraEvents {
    'cart:updated': { count: number }
    'user:login':   { id: number; name: string }
    'modal:close':  void
  }
}
Micra.emit('cart:updated', { count: 3 })   // ✓ typed
Micra.emit('cart:updated', { count: '3' }) // ✗ type error
Micra.emit('cart:updated')                 // ✗ payload required
Micra.emit('modal:close')                  // ✓ void payload — no args
Micra.on('user:login', user => user.name)  // user: { id, name }

A payload type that includes undefined (e.g. { x?: T } | undefined) makes the emit argument optional. Use void for events that carry no payload at all.

EventPayload

type EventPayload<K extends string> = K extends keyof MicraEvents
  ? MicraEvents[K]
  : unknown;

Resolves the handler payload type for an event name.

EmitArgs

type EmitArgs<K extends string> = K extends keyof MicraEvents
  ? [MicraEvents[K]] extends [void]
    ? [payload?: undefined]
    : undefined extends MicraEvents[K]
      ? [payload?: MicraEvents[K]]
      : [payload: MicraEvents[K]]
  : [payload?: unknown];

Tuple of arguments accepted by emit after the event name. Encodes the rules described under MicraEvents: required payload for declared non-void events, optional otherwise.

FetchOptions

interface FetchOptions {
  method?: string;
  headers?: Record<string, string>;
  body?: unknown;
  [key: string]: unknown;
}

Options for this.fetch().

Notes:

MicraConfig

interface MicraConfig {
  sanitize?: (html: string) => string;
}

Options for Micra.config(). sanitize runs on every data-html value before it is written.

ComponentDefinition

type ComponentDefinition<S extends StateRecord = StateRecord> = {
  state?: S;
  onCreate?: () => void | Promise<void>;
  onDestroy?: () => void;
  [method: string]: unknown;
} & ThisType<ComponentInstance<S>>;

Defines state, lifecycle hooks, and methods for a component.

Example:

const modal = Micra.defineComponent({
  state: { open: false },
  open() {
    this.state.open = true;
  },
  close() {
    this.state.open = false;
  },
});

ComponentInstance

interface ComponentInstance<S extends StateRecord = StateRecord> {
  readonly $el: HTMLElement;
  state: S;
  refs: Record<string, HTMLElement>;
  render(): void;
  destroy(): void;
  prop(name: string): string | undefined;
  prop<T>(name: string, defaultVal: T): T;
  fetch(url: string, options?: FetchOptions): Promise<unknown>;
  emit(event: string, payload?: unknown): void;
  on<T = unknown>(event: string, handler: EventHandler<T>): UnsubFn;
}

Public instance shape available as this inside methods.

FetchError

class FetchError extends Error {
  readonly status: number;
  readonly response: Response;
}

Thrown by this.fetch() when the response is not 2xx.

Example:

import { FetchError } from "micra";

try {
  await this.fetch("/api/users/404");
} catch (error) {
  if (error instanceof FetchError) {
    console.log(error.status);
  }
}