API Reference
@arrow-js/core
reactive()
Creates observable state or computed values.
Signatures
import type { interface Computed<TValue>Computed, type Reactive<T extends ReactiveTarget> = { [P in keyof T]: T[P] extends Computed<infer TValue> ? TValue : T[P] extends ReactiveTarget ? T[P] | Reactive<T[P]> : T[P]; } & {
$on: <P extends keyof T>(property: P, callback: (value?: T[P], oldValue?: T[P]) => void) => void;
$off: <P extends keyof T>(property: P, callback: (value?: T[P], oldValue?: T[P]) => void) => void;
}
Reactive, type ReactiveTarget = Record<PropertyKey, unknown> | unknown[]ReactiveTarget } from '@arrow-js/core'
// Observable state
declare function function reactive<T extends ReactiveTarget>(data: T): Reactive<T> (+1 overload)reactive<function (type parameter) T in reactive<T extends ReactiveTarget>(data: T): Reactive<T>T extends type ReactiveTarget = Record<PropertyKey, unknown> | unknown[]ReactiveTarget>(data: T extends ReactiveTargetdata: function (type parameter) T in reactive<T extends ReactiveTarget>(data: T): Reactive<T>T): type Reactive<T extends ReactiveTarget> = { [P in keyof T]: T[P] extends Computed<infer TValue> ? TValue : T[P] extends ReactiveTarget ? T[P] | Reactive<T[P]> : T[P]; } & {
$on: <P extends keyof T>(property: P, callback: (value?: T[P], oldValue?: T[P]) => void) => void;
$off: <P extends keyof T>(property: P, callback: (value?: T[P], oldValue?: T[P]) => void) => void;
}
Reactive<function (type parameter) T in reactive<T extends ReactiveTarget>(data: T): Reactive<T>T>
// Computed value
declare function function reactive<T>(effect: () => T): Computed<T> (+1 overload)reactive<function (type parameter) T in reactive<T>(effect: () => T): Computed<T>T>(effect: () => Teffect: () => function (type parameter) T in reactive<T>(effect: () => T): Computed<T>T): interface Computed<TValue>Computed<function (type parameter) T in reactive<T>(effect: () => T): Computed<T>T>
Observable state
Pass an object or array to get a reactive proxy. Property reads are tracked inside watchers and template expressions. Property writes notify observers.
import { function reactive<T extends ReactiveTarget>(data: T): Reactive<T> (+1 overload)reactive } from '@arrow-js/core'
const const data: Reactive<{
count: number;
items: string[];
}>
data = reactive<{
count: number;
items: string[];
}>(data: {
count: number;
items: string[];
}): Reactive<{
count: number;
items: string[];
}> (+1 overload)
reactive({ count: numbercount: 0, items: string[]items: [] as string[] })
const data: Reactive<{
count: number;
items: string[];
}>
data.count: numbercount++ // triggers observers
const data: Reactive<{
count: number;
items: string[];
}>
data.items: string[] | Reactive<string[]>items.Array<T>.push(...items: string[]): numberAppends new elements to the end of an array, and returns the new length of the array.push('hello') // array mutations trigger parent observers
Computed values
Pass an arrow function to create a computed value. The expression re-evaluates when tracked reads change.
import { function reactive<T extends ReactiveTarget>(data: T): Reactive<T> (+1 overload)reactive } from '@arrow-js/core'
const const props: Reactive<{
count: number;
multiplier: number;
}>
props = reactive<{
count: number;
multiplier: number;
}>(data: {
count: number;
multiplier: number;
}): Reactive<{
count: number;
multiplier: number;
}> (+1 overload)
reactive({ count: numbercount: 2, multiplier: numbermultiplier: 10 })
const const data: Reactive<{
total: Computed<number>;
}>
data = reactive<{
total: Computed<number>;
}>(data: {
total: Computed<number>;
}): Reactive<{
total: Computed<number>;
}> (+1 overload)
reactive({
total: Computed<number>total: reactive<number>(effect: () => number): Computed<number> (+1 overload)reactive(() => const props: Reactive<{
count: number;
multiplier: number;
}>
props.count: numbercount * const props: Reactive<{
count: number;
multiplier: number;
}>
props.multiplier: numbermultiplier)
})
const data: Reactive<{
total: Computed<number>;
}>
data.total: numbertotal // 20 — reads like a normal value, auto-updates
Manual subscriptions
Prefer watch() or template expressions over manual
subscriptions. Use $on/$off only when
you need direct per-property control.
- Only objects and arrays can be reactive. Primitives cannot.
- Nested objects are lazily made reactive on first access.
-
reactive()on an already-reactive object returns the same proxy (idempotent).
watch()
Runs side effects that re-execute when tracked reactive reads change.
Signatures
// Single-effect form
declare function function watch<F extends () => unknown>(effect: F): [returnValue: ReturnType<F>, stop: () => void] (+1 overload)watch<function (type parameter) F in watch<F extends () => unknown>(effect: F): [returnValue: ReturnType<F>, stop: () => void]F extends () => unknown>(
effect: F extends () => unknowneffect: function (type parameter) F in watch<F extends () => unknown>(effect: F): [returnValue: ReturnType<F>, stop: () => void]F
): [ReturnType<F>returnValue: type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : anyObtain the return type of a function typeReturnType<function (type parameter) F in watch<F extends () => unknown>(effect: F): [returnValue: ReturnType<F>, stop: () => void]F>, function () => voidstop: () => void]
// Getter + afterEffect form
declare function function watch<F extends () => unknown, A extends (arg: ReturnType<F>) => unknown>(effect: F, afterEffect: A): [returnValue: ReturnType<A>, stop: () => void] (+1 overload)watch<function (type parameter) F in watch<F extends () => unknown, A extends (arg: ReturnType<F>) => unknown>(effect: F, afterEffect: A): [returnValue: ReturnType<A>, stop: () => void]F extends () => unknown, function (type parameter) A in watch<F extends () => unknown, A extends (arg: ReturnType<F>) => unknown>(effect: F, afterEffect: A): [returnValue: ReturnType<A>, stop: () => void]A extends (arg: ReturnType<F>arg: type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : anyObtain the return type of a function typeReturnType<function (type parameter) F in watch<F extends () => unknown, A extends (arg: ReturnType<F>) => unknown>(effect: F, afterEffect: A): [returnValue: ReturnType<A>, stop: () => void]F>) => unknown>(
effect: F extends () => unknowneffect: function (type parameter) F in watch<F extends () => unknown, A extends (arg: ReturnType<F>) => unknown>(effect: F, afterEffect: A): [returnValue: ReturnType<A>, stop: () => void]F,
afterEffect: A extends (arg: ReturnType<F>) => unknownafterEffect: function (type parameter) A in watch<F extends () => unknown, A extends (arg: ReturnType<F>) => unknown>(effect: F, afterEffect: A): [returnValue: ReturnType<A>, stop: () => void]A
): [ReturnType<A>returnValue: type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : anyObtain the return type of a function typeReturnType<function (type parameter) A in watch<F extends () => unknown, A extends (arg: ReturnType<F>) => unknown>(effect: F, afterEffect: A): [returnValue: ReturnType<A>, stop: () => void]A>, function () => voidstop: () => void]
Parameters
-
effect— A function that reads reactive properties. Runs immediately on creation. In the single-effect form, this is both the tracker and the side effect. -
afterEffect(optional) — Receives the return value ofeffect. Only theeffectfunction tracks dependencies; theafterEffectruns after dependency collection.
Returns
A tuple [returnValue, stop]. Call stop()
to unsubscribe from all tracked dependencies.
When a watcher is created inside component(), Arrow
also stops it automatically when that component unmounts.
Examples
import { function reactive<T extends ReactiveTarget>(data: T): Reactive<T> (+1 overload)reactive, function watch<F extends (...args: unknown[]) => unknown>(effect: F): [returnValue: ReturnType<F>, stop: () => void] (+1 overload)watch } from '@arrow-js/core'
const const data: Reactive<{
price: number;
quantity: number;
}>
data = reactive<{
price: number;
quantity: number;
}>(data: {
price: number;
quantity: number;
}): Reactive<{
price: number;
quantity: number;
}> (+1 overload)
reactive({ price: numberprice: 25, quantity: numberquantity: 10 })
// Single-effect: tracks and runs in one function
const [, const stop: () => voidstop] = watch<() => void>(effect: () => void): [returnValue: void, stop: () => void] (+1 overload)watch(() => {
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(`Total: ${const data: Reactive<{
price: number;
quantity: number;
}>
data.price: numberprice * const data: Reactive<{
price: number;
quantity: number;
}>
data.quantity: numberquantity}`)
})
// Getter + effect: separates tracking from side effect
watch<() => number, (total: number) => void>(effect: () => number, afterEffect: (total: number) => void): [returnValue: void, stop: () => void] (+1 overload)watch(
() => const data: Reactive<{
price: number;
quantity: number;
}>
data.price: numberprice * const data: Reactive<{
price: number;
quantity: number;
}>
data.quantity: numberquantity,
(total: numbertotal) => var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(`Total: ${total: numbertotal}`)
)
// Stop watching
const stop: () => voidstop()
- Dependencies are auto-discovered from reactive reads.
- Dependencies no longer read on subsequent runs are dropped.
html
Tagged template literal that creates an ArrowTemplate.
Signature
import type { type ArrowExpression = ArrowRenderable | ((...args: unknown[]) => ArrowRenderable) | EventListener | ((evt: InputEvent) => void)ArrowExpression, ArrowTemplate } from '@arrow-js/core'
declare function function html(strings: TemplateStringsArray | string[], ...expSlots: ArrowExpression[]): ArrowTemplatehtml(
strings: TemplateStringsArray | string[]strings: TemplateStringsArray | string[],
...expSlots: ArrowExpression[]expSlots: type ArrowExpression = ArrowRenderable | ((...args: unknown[]) => ArrowRenderable) | EventListener | ((evt: InputEvent) => void)ArrowExpression[]
): ArrowTemplate
Compiler output can call html(strings, ...exprs) directly
with a generated string[]. Arrow will route it through the
same template caching, pooling, hydration, and cleanup path as a
tagged literal.
Mounting
An ArrowTemplate is callable. Pass a parent node to
mount into the DOM, or call with no arguments to get a
DocumentFragment.
const const template: ArrowTemplatetemplate = const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<h1>Hello</h1>`
// Mount to a DOM node
const template: ArrowTemplate
(parent: ParentNode) => ParentNode (+1 overload)
template(var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.getElementById(elementId: string): HTMLElement | nullReturns the first element within node's descendants whose ID is elementId.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/getElementById)getElementById('app'))
// Get a DocumentFragment
const const fragment: DocumentFragmentfragment = const template: ArrowTemplate
() => DocumentFragment (+1 overload)
template()
Expression types
-
Static — Any non-function value. Renders once.
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<p>${const someString: stringsomeString}</p>` -
Reactive — A function expression. Re-evaluates
when tracked reads change.
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<p>${() =>data.const data: { active: boolean; boundaries: string[]; count: number; list: string[]; loading: boolean; logTotal: boolean; price: number; quantity: number; text: string; user: { last: string; }; }count: numbercount}</p>` -
Template / component — Nest directly.
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<div>${const otherTemplate: ArrowTemplateotherTemplate}</div>`const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<div>${MyComponent({function MyComponent(props: { label: string; }): unknownlabel: stringlabel: 'hi' })}</div>` -
Array — Renders a list of templates.
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<ul>${() =>items.const items: { id: string | number; label: string; name: string; }[]Array<{ id: string | number; label: string; name: string; }>.map<ArrowTemplate>(callbackfn: (value: { id: string | number; label: string; name: string; }, index: number, array: { id: string | number; label: string; name: string; }[]) => ArrowTemplate, thisArg?: any): ArrowTemplate[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map(i =>i: { id: string | number; label: string; name: string; }const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<li>${i.i: { id: string | number; label: string; name: string; }name: stringname}</li>`)}</ul>`
Attribute binding
Static or reactive. Return false to remove the attribute.
// Static
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<div class="${const cls: stringcls}"></div>`
// Reactive
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<div class="${() => const data: {
active: boolean;
boundaries: string[];
count: number;
list: string[];
loading: boolean;
logTotal: boolean;
price: number;
quantity: number;
text: string;
user: {
last: string;
};
}
data.active: booleanactive ? 'on' : 'off'}"></div>`
// Boolean removal
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<button disabled="${() => const data: {
active: boolean;
boundaries: string[];
count: number;
list: string[];
loading: boolean;
logTotal: boolean;
price: number;
quantity: number;
text: string;
user: {
last: string;
};
}
data.loading: booleanloading ? '' : false}">Submit</button>`
Property binding
Prefix with . to set an IDL property instead of an attribute.
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<input .value="${() => const data: {
active: boolean;
boundaries: string[];
count: number;
list: string[];
loading: boolean;
logTotal: boolean;
price: number;
quantity: number;
text: string;
user: {
last: string;
};
}
data.text: stringtext}" />`
Event binding
Prefix with @ to attach an event listener.
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<button @click="${(e: anye) => function handleClick(event: Event): voidhandleClick(e: anye)}">Click</button>`
List keys
Call .key() on a template to give it stable identity
in a list. Without keys, list patches reuse slots by position.
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<ul>${() => const items: {
id: string | number;
label: string;
name: string;
}[]
items.Array<{ id: string | number; label: string; name: string; }>.map<ArrowTemplate>(callbackfn: (value: {
id: string | number;
label: string;
name: string;
}, index: number, array: {
id: string | number;
label: string;
name: string;
}[]) => ArrowTemplate, thisArg?: any): ArrowTemplate[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.map(item: {
id: string | number;
label: string;
name: string;
}
item =>
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`<li>${item: {
id: string | number;
label: string;
name: string;
}
item.name: stringname}</li>`.ArrowTemplate.key: (key: string | number | undefined) => ArrowTemplatekey(item: {
id: string | number;
label: string;
name: string;
}
item.id: string | numberid)
)}</ul>`
component()
Wraps a factory function to provide stable local state across parent re-renders.
Signatures
import type {
ArrowTemplate,
AsyncComponentOptions,
Component,
ComponentWithProps,
Props,
ReactiveTarget,
} from '@arrow-js/core'
// Sync — no props
declare function component(
factory: () => ArrowTemplate
): Component
// Sync — with props
declare function component<T extends ReactiveTarget>(
factory: (props: Props<T>) => ArrowTemplate
): ComponentWithProps<T>
// Async — no props
declare function component<TValue, TSnapshot = TValue>(
factory: () => Promise<TValue> | TValue,
options?: AsyncComponentOptions<ReactiveTarget, TValue, TSnapshot>
): Component
// Async — with props
declare function component<T extends ReactiveTarget, TValue, TSnapshot = TValue>(
factory: (props: Props<T>) => Promise<TValue> | TValue,
options?: AsyncComponentOptions<T, TValue, TSnapshot>
): ComponentWithProps<T>
AsyncComponentOptions
import type { interface AsyncComponentOptions<T extends ReactiveTarget, TValue, TEvents extends EventMap = EventMap, TSnapshot = TValue>AsyncComponentOptions } from '@arrow-js/core'
Usage
import { function component(factory: () => ArrowTemplate): Component (+7 overloads)component, function html(strings: TemplateStringsArray | string[], ...expressions: unknown[]): ArrowTemplatehtml, function onCleanup(fn: () => void): () => voidonCleanup, function reactive<T extends ReactiveTarget>(data: T): Reactive<T> (+1 overload)reactive } from '@arrow-js/core'
import type { type Props<T extends ReactiveTarget> = { [P in keyof T]: T[P] extends ReactiveTarget ? T[P] | Props<T[P]> : T[P]; }Props } from '@arrow-js/core'
// Sync component with props
const const Counter: Component<EventMap>Counter = component<EventMap>(factory: (props: undefined, emit: Emit<EventMap>) => ArrowTemplate): Component<EventMap> (+7 overloads)component((props: Props<{
count: number;
}>
props: type Props<T extends ReactiveTarget> = { [P in keyof T]: T[P] extends ReactiveTarget ? T[P] | Props<T[P]> : T[P]; }Props<{ count: numbercount: number }>) => {
const const local: Reactive<{
clicks: number;
}>
local = reactive<{
clicks: number;
}>(data: {
clicks: number;
}): Reactive<{
clicks: number;
}> (+1 overload)
reactive({ clicks: numberclicks: 0 })
const const onResize: () => voidonResize = () => var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log('resize')
var window: Window & typeof globalThisThe **`window`** property of a Window object points to the window object itself.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/window)window.addEventListener<"resize">(type: "resize", listener: (this: Window, ev: UIEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)The **`addEventListener()`** method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener)addEventListener('resize', const onResize: () => voidonResize)
function onCleanup(fn: () => void): () => voidonCleanup(() => var window: Window & typeof globalThisThe **`window`** property of a Window object points to the window object itself.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/window)window.removeEventListener<"resize">(type: "resize", listener: (this: Window, ev: UIEvent) => any, options?: boolean | EventListenerOptions): void (+1 overload)The **`removeEventListener()`** method of the EventTarget interface removes an event listener previously registered with EventTarget.addEventListener() from the target.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener)removeEventListener('resize', const onResize: () => voidonResize))
return function html(strings: TemplateStringsArray | string[], ...expressions: unknown[]): ArrowTemplatehtml`<button @click="${() => const local: Reactive<{
clicks: number;
}>
local.clicks: numberclicks++}">
Root ${() => props: Props<{
count: number;
}>
props.count: numbercount} | Local ${() => const local: Reactive<{
clicks: number;
}>
local.clicks: numberclicks}
</button>`
})
// Async component
const const UserName: Component<EventMap>UserName = component<any, EventMap, any>(factory: (props: undefined, emit: Emit<EventMap>) => any, options?: AsyncComponentOptions<ReactiveTarget, any, EventMap, any>): Component<EventMap> (+7 overloads)component(async ({ id: stringid }: { id: stringid: string }) => {
const const user: anyuser = await function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch)fetch(`/api/users/${id: stringid}`).Promise<Response>.then<any, never>(onfulfilled?: (value: Response) => any, onrejected?: (reason: any) => PromiseLike<never>): Promise<any>Attaches callbacks for the resolution and/or rejection of the Promise.then(r: Responser => r: Responser.Body.json(): Promise<any>[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)json())
return const user: anyuser.name
})
.key() for lists
Call .key() on the component call to preserve identity
when rendering in a keyed list.
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`${() => const items: {
id: string | number;
label: string;
name: string;
}[]
items.Array<{ id: string | number; label: string; name: string; }>.map<unknown>(callbackfn: (value: {
id: string | number;
label: string;
name: string;
}, index: number, array: {
id: string | number;
label: string;
name: string;
}[]) => unknown, thisArg?: any): unknown[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.map(item: {
id: string | number;
label: string;
name: string;
}
item =>
function ItemCard(props: {
id: string | number;
name: string;
}): {
key(key: string | number | undefined): unknown;
}
ItemCard(item: {
id: string | number;
label: string;
name: string;
}
item).function key(key: string | number | undefined): unknownkey(item: {
id: string | number;
label: string;
name: string;
}
item.id: string | numberid)
)}`
- The factory runs once per slot, not on every update.
- Never destructure props at the top of the factory — read them lazily inside reactive expressions.
-
Props<T>is a live proxy over the source object, so checks like'count' in propsandObject.keys(props)reflect the current source keys. - SSR waits for all async components to resolve before returning HTML.
- JSON-safe async results are auto-serialized into the hydration payload.
onCleanup()
Registers teardown work for the current component instance.
Signature
declare function function onCleanup(fn: () => void): () => voidonCleanup(fn: () => voidfn: () => void): () => void
Behavior
-
Call it inside
component()while setting up local side effects. - Arrow runs the cleanup automatically when that component slot unmounts.
- It also returns a disposer so you can stop the side effect early.
Example
import { function component(factory: () => ArrowTemplate): Component (+7 overloads)component, function html(strings: TemplateStringsArray | string[], ...expressions: unknown[]): ArrowTemplatehtml, function onCleanup(fn: () => void): () => voidonCleanup } from '@arrow-js/core'
const const ResizeProbe: Component<EventMap>ResizeProbe = function component(factory: () => ArrowTemplate): Component (+7 overloads)component(() => {
const const onResize: () => voidonResize = () => var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(var window: Window & typeof globalThisThe **`window`** property of a Window object points to the window object itself.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/window)window.innerWidth: numberThe read-only Window property **`innerWidth`** returns the interior width of the window in pixels (that is, the width of the window's layout viewport).
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/innerWidth)innerWidth)
var window: Window & typeof globalThisThe **`window`** property of a Window object points to the window object itself.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/window)window.addEventListener<"resize">(type: "resize", listener: (this: Window, ev: UIEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)The **`addEventListener()`** method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener)addEventListener('resize', const onResize: () => voidonResize)
function onCleanup(fn: () => void): () => voidonCleanup(() => var window: Window & typeof globalThisThe **`window`** property of a Window object points to the window object itself.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/window)window.removeEventListener<"resize">(type: "resize", listener: (this: Window, ev: UIEvent) => any, options?: boolean | EventListenerOptions): void (+1 overload)The **`removeEventListener()`** method of the EventTarget interface removes an event listener previously registered with EventTarget.addEventListener() from the target.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener)removeEventListener('resize', const onResize: () => voidonResize))
return function html(strings: TemplateStringsArray | string[], ...expressions: unknown[]): ArrowTemplatehtml`<div>Watching resize…</div>`
})
Use onCleanup() for manual subscriptions like DOM
listeners, timers, sockets, or anything else Arrow did not create
for you.
pick() / props()
Narrows a reactive object down to specific keys. props
is an alias for pick.
Signatures
declare function function pick<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K> (+1 overload)pick<function (type parameter) T in pick<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K>T extends object, function (type parameter) K in pick<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K>K extends keyof function (type parameter) T in pick<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K>T>(
source: T extends objectsource: function (type parameter) T in pick<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K>T,
...keys: K[]keys: function (type parameter) K in pick<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K>K[]
): type Pick<T, K extends keyof T> = { [P in K]: T[P]; }From T, pick a set of properties whose keys are in the union KPick<function (type parameter) T in pick<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K>T, function (type parameter) K in pick<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K>K>
declare function function pick<T extends object>(source: T): T (+1 overload)pick<function (type parameter) T in pick<T extends object>(source: T): TT extends object>(source: T extends objectsource: function (type parameter) T in pick<T extends object>(source: T): TT): function (type parameter) T in pick<T extends object>(source: T): TT
const const props: {
<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K>;
<T extends object>(source: T): T;
}
props = function pick<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K> (+1 overload)pick // alias
Usage
import { function pick<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K> (+1 overload)pick, function reactive<T extends ReactiveTarget>(data: T): Reactive<T> (+1 overload)reactive } from '@arrow-js/core'
const const state: Reactive<{
count: number;
theme: string;
locale: string;
}>
state = reactive<{
count: number;
theme: string;
locale: string;
}>(data: {
count: number;
theme: string;
locale: string;
}): Reactive<{
count: number;
theme: string;
locale: string;
}> (+1 overload)
reactive({ count: numbercount: 1, theme: stringtheme: 'dark', locale: stringlocale: 'en' })
// Pass only the keys a component needs
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`${function Counter(props: {
count: number;
}): unknown
Counter(pick<Reactive<{
count: number;
theme: string;
locale: string;
}>, "count">(source: Reactive<{
count: number;
theme: string;
locale: string;
}>, ...keys: "count"[]): Pick<Reactive<{
count: number;
theme: string;
locale: string;
}>, "count"> (+1 overload)
pick(const state: Reactive<{
count: number;
theme: string;
locale: string;
}>
state, 'count'))}`
// Without keys — returns the source as-is
const html: (strings: TemplateStringsArray | string[], ...expressions: unknown[]) => ArrowTemplatehtml`${function Counter(props: {
count: number;
}): unknown
Counter(pick<Reactive<{
count: number;
theme: string;
locale: string;
}>, "count">(source: Reactive<{
count: number;
theme: string;
locale: string;
}>, ...keys: "count"[]): Pick<Reactive<{
count: number;
theme: string;
locale: string;
}>, "count"> (+1 overload)
pick(const state: Reactive<{
count: number;
theme: string;
locale: string;
}>
state))}`
The returned object is a live proxy — reads and writes flow through to the source. It is not a copy.
nextTick()
Flushes Arrow's internal microtask queue, then runs an optional callback.
Signature
declare function function nextTick(fn?: CallableFunction): Promise<unknown>nextTick(fn: CallableFunctionfn?: CallableFunction): interface Promise<T>Represents the completion of an asynchronous operationPromise<unknown>
Usage
import { function nextTick(fn?: CallableFunction): Promise<unknown>nextTick, function reactive<T extends ReactiveTarget>(data: T): Reactive<T> (+1 overload)reactive } from '@arrow-js/core'
const const data: Reactive<{
count: number;
}>
data = reactive<{
count: number;
}>(data: {
count: number;
}): Reactive<{
count: number;
}> (+1 overload)
reactive({ count: numbercount: 0 })
const data: Reactive<{
count: number;
}>
data.count: numbercount = 5
// Wait for all pending reactive updates to flush
await function nextTick(fn?: CallableFunction): Promise<unknown>nextTick()
// DOM is now updated
// Or pass a callback
function nextTick(fn?: CallableFunction): Promise<unknown>nextTick(() => {
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log('DOM updated')
})
Arrow batches reactive updates into a microtask.
nextTick lets you wait for that flush before reading
the DOM or performing follow-up work.
@arrow-js/framework
render()
Full-lifecycle render that mounts a view into a root element, tracking async components and boundaries.
Signature
import type { RenderOptions, RenderResult } from '@arrow-js/framework'
declare function function render(root: ParentNode, view: unknown, options?: RenderOptions): Promise<RenderResult>render(
root: ParentNoderoot: ParentNode,
view: unknownview: unknown,
options: RenderOptionsoptions?: RenderOptions
): interface Promise<T>Represents the completion of an asynchronous operationPromise<RenderResult>
RenderOptions
import type { RenderOptions } from '@arrow-js/framework'
RenderResult
import type { RenderPayload, RenderResult } from '@arrow-js/framework'
Usage
import { function render(root: ParentNode, view: unknown, options?: RenderOptions): Promise<RenderResult>render } from '@arrow-js/framework'
import { function html(strings: TemplateStringsArray | string[], ...expressions: unknown[]): ArrowTemplatehtml } from '@arrow-js/core'
const const view: ArrowTemplateview = function html(strings: TemplateStringsArray | string[], ...expressions: unknown[]): ArrowTemplatehtml`<h1>Hello</h1>`
const { const root: ParentNoderoot, const payload: RenderPayloadpayload } = await function render(root: ParentNode, view: unknown, options?: RenderOptions): Promise<RenderResult>render(var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.getElementById(elementId: string): HTMLElement | nullReturns the first element within node's descendants whose ID is elementId.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/getElementById)getElementById('app'), const view: ArrowTemplateview)
boundary()
Wraps a view in hydration boundary markers, enabling targeted recovery during hydration.
Signature
import type { ArrowTemplate } from '@arrow-js/core'
import type { BoundaryOptions } from '@arrow-js/framework'
declare function function boundary(view: unknown, options?: BoundaryOptions): ArrowTemplateboundary(
view: unknownview: unknown,
options: BoundaryOptionsoptions?: BoundaryOptions
): ArrowTemplate
Usage
import { function boundary(view: unknown, options?: BoundaryOptions): ArrowTemplateboundary } from '@arrow-js/framework'
import { function html(strings: TemplateStringsArray | string[], ...expressions: unknown[]): ArrowTemplatehtml } from '@arrow-js/core'
function html(strings: TemplateStringsArray | string[], ...expressions: unknown[]): ArrowTemplatehtml`
<main>
${function boundary(view: unknown, options?: BoundaryOptions): ArrowTemplateboundary(function Sidebar(): unknownSidebar(), { BoundaryOptions.idPrefix?: stringidPrefix: 'sidebar' })}
${function boundary(view: unknown, options?: BoundaryOptions): ArrowTemplateboundary(function Content(): unknownContent(), { BoundaryOptions.idPrefix?: stringidPrefix: 'content' })}
</main>
`
This inserts <template data-arrow-boundary-start/end>
markers in the HTML. During hydration, if a subtree mismatches,
Arrow repairs that boundary region instead of replacing the
entire root.
Always wrap async components in a boundary for SSR/hydration recovery. Also useful around any subtree that may diverge between server and client (e.g. time-dependent content).
toTemplate()
Normalizes any view value into an ArrowTemplate.
Useful when you have a value that might be a string, number,
template, or component call and need a consistent template type.
Signature
import type { ArrowTemplate } from '@arrow-js/core'
declare function function toTemplate(view: unknown): ArrowTemplatetoTemplate(view: unknownview: unknown): ArrowTemplate
Usage
import { function toTemplate(view: unknown): ArrowTemplatetoTemplate } from '@arrow-js/framework'
import { function html(strings: TemplateStringsArray | string[], ...expressions: unknown[]): ArrowTemplatehtml } from '@arrow-js/core'
const const view: "Hello, world"view = 'Hello, world'
const const template: ArrowTemplatetemplate = function toTemplate(view: unknown): ArrowTemplatetoTemplate(const view: "Hello, world"view)
// Now usable anywhere an ArrowTemplate is expected
const template: ArrowTemplate
(parent: ParentNode) => ParentNode (+1 overload)
template(var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.getElementById(elementId: string): HTMLElement | nullReturns the first element within node's descendants whose ID is elementId.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/getElementById)getElementById('app'))
renderDocument()
Injects rendered HTML, head content, and payload script into an HTML shell template string. Used in custom server setups.
Signature
import type { DocumentRenderParts } from '@arrow-js/framework'
declare function function renderDocument(template: string, parts: DocumentRenderParts): stringrenderDocument(
template: stringtemplate: string,
parts: DocumentRenderPartsparts: DocumentRenderParts
): string
Placeholder markers
The template string should contain these HTML comment
placeholders:
<!--app-head-->— replaced withparts.head<!--app-html-->— replaced withparts.html<!--app-payload-->— replaced withparts.payloadScript
@arrow-js/ssr
renderToString()
Renders a view to an HTML string on the server. Waits for all async components to resolve before returning.
This is the main SSR entry point. Use it inside your request handler after you have chosen the page and built the Arrow view for the incoming URL.
Signature
import type {
HydrationPayload,
SsrRenderOptions,
SsrRenderResult,
} from '@arrow-js/ssr'
declare function function renderToString(view: unknown, options?: SsrRenderOptions): Promise<SsrRenderResult>renderToString(
view: unknownview: unknown,
options: SsrRenderOptionsoptions?: SsrRenderOptions
): interface Promise<T>Represents the completion of an asynchronous operationPromise<SsrRenderResult>
Usage
import { function renderToString(view: unknown, options?: SsrRenderOptions): Promise<SsrRenderResult>renderToString, function serializePayload(payload: unknown, id?: string): stringserializePayload } from '@arrow-js/ssr'
const { const html: stringhtml, const payload: HydrationPayloadpayload } = await function renderToString(view: unknown, options?: SsrRenderOptions): Promise<SsrRenderResult>renderToString(const view: unknownview)
// Serialize payload for client-side hydration
const const script: stringscript = function serializePayload(payload: unknown, id?: string): stringserializePayload(const payload: HydrationPayloadpayload)
Typical server flow
import { renderToString, serializePayload } from '@arrow-js/ssr'
export async function renderPage(url: string) {
const page = routeToPage(url)
const result = await renderToString(page.view)
return [
'<!doctype html>',
'<html>',
' <head>',
` <title>${page.title}</title>`,
' </head>',
' <body>',
` <div id="app">${result.html}</div>`,
` ${serializePayload(result.payload)}`,
' </body>',
'</html>'
].join('\n')
}
Internally uses JSDOM to render templates into a virtual DOM, then serializes the result. All async components are awaited and their results captured in the payload.
serializePayload()
Serializes a hydration payload into a
<script type="application/json"> tag that can
be embedded in the HTML document.
Signature
declare function function serializePayload(payload: unknown, id?: string): stringserializePayload(
payload: unknownpayload: unknown,
id: stringid?: string // default: 'arrow-ssr-payload'
): string
Returns
An HTML string containing a <script> tag with
the JSON-serialized payload. The id attribute matches
what readPayload() looks for on the client.
const const payload: {
rootId: string;
async: {};
boundaries: any[];
}
payload = { rootId: stringrootId: 'app', async: {}async: {}, boundaries: undefined[]boundaries: [] }
const const defaultScript: stringdefaultScript = const serializePayload: (payload: unknown, id?: string) => stringserializePayload(const payload: {
rootId: string;
async: {};
boundaries: any[];
}
payload)
// <script id="arrow-ssr-payload" type="application/json">{...}</script>
// Custom id
const const customScript: stringcustomScript = const serializePayload: (payload: unknown, id?: string) => stringserializePayload(const payload: {
rootId: string;
async: {};
boundaries: any[];
}
payload, 'my-payload')
// <script id="my-payload" type="application/json">{...}</script>
The serializer escapes </script> sequences
inside the JSON to prevent injection.
@arrow-js/hydrate
hydrate()
Reconciles server-rendered HTML with the client-side view tree, reconnecting reactivity without replacing existing DOM nodes.
Call it once in your browser entry after reading the server payload and rebuilding the same page view that was used during SSR.
Signature
import type {
HydrationOptions,
HydrationPayload,
HydrationResult,
} from '@arrow-js/hydrate'
declare function function hydrate(root: ParentNode, view: unknown, payload?: HydrationPayload, options?: HydrationOptions): Promise<HydrationResult>hydrate(
root: ParentNoderoot: ParentNode,
view: unknownview: unknown,
payload: HydrationPayloadpayload?: HydrationPayload,
options: HydrationOptionsoptions?: HydrationOptions
): interface Promise<T>Represents the completion of an asynchronous operationPromise<HydrationResult>
HydrationOptions
import type {
HydrationMismatchDetails,
HydrationOptions,
} from '@arrow-js/hydrate'
HydrationResult
import type { HydrationResult } from '@arrow-js/hydrate'
Usage
import { function hydrate(root: ParentNode, view: unknown, payload?: HydrationPayload, options?: HydrationOptions): Promise<HydrationResult>hydrate, function readPayload(doc?: Document, id?: string): HydrationPayloadreadPayload } from '@arrow-js/hydrate'
import { function createApp(): ArrowTemplatecreateApp } from './app'
const const payload: HydrationPayloadpayload = function readPayload(doc?: Document, id?: string): HydrationPayloadreadPayload()
const const root: HTMLElementroot = var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.getElementById(elementId: string): HTMLElement | nullReturns the first element within node's descendants whose ID is elementId.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/getElementById)getElementById('app')!
const const result: HydrationResultresult = await function hydrate(root: ParentNode, view: unknown, payload?: HydrationPayload, options?: HydrationOptions): Promise<HydrationResult>hydrate(const root: HTMLElementroot, function createApp(): ArrowTemplatecreateApp(), const payload: HydrationPayloadpayload, {
HydrationOptions.onMismatch?: (details: HydrationMismatchDetails) => voidonMismatch: (details: HydrationMismatchDetailsdetails) => {
var console: Consoleconsole.Console.warn(...data: any[]): voidThe **`console.warn()`** static method outputs a warning message to the console at the 'warning' log level.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/warn_static)warn('Hydration mismatch:', details: HydrationMismatchDetailsdetails)
}
})
Typical client flow
import { hydrate, readPayload } from '@arrow-js/hydrate'
const payload = readPayload()
const root = document.getElementById(payload.rootId ?? 'app')
if (!root) {
throw new Error('Missing #app root')
}
await hydrate(
root,
routeToPage(window.location.pathname).view,
payload
)
When the server HTML matches the client view, Arrow adopts the existing DOM nodes and attaches reactive bindings. When a mismatch is detected, boundary regions are repaired individually before falling back to a full root replacement.
readPayload()
Reads the hydration payload from a
<script type="application/json"> tag embedded
in the document by the server.
Signature
declare function function readPayload(doc?: Document, id?: string): HydrationPayloadreadPayload(
doc: Documentdoc?: Document, // default: document
id: stringid?: string // default: 'arrow-ssr-payload'
): type HydrationPayload = HydrationPayloadHydrationPayload
Usage
import { function readPayload(doc?: Document, id?: string): HydrationPayloadreadPayload } from '@arrow-js/hydrate'
// Default — reads from document, id="arrow-ssr-payload"
const const payload: HydrationPayloadpayload = function readPayload(doc?: Document, id?: string): HydrationPayloadreadPayload()
// Custom document and id
const const iframe: HTMLIFrameElementiframe = var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.ParentNode.querySelector<"iframe">(selectors: "iframe"): HTMLIFrameElement (+4 overloads)Returns the first element that is a descendant of node that matches selectors.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/querySelector)querySelector('iframe')
const const payloadFromFrame: HydrationPayloadpayloadFromFrame = const iframe: HTMLIFrameElementiframe?.HTMLIFrameElement.contentDocument: DocumentIf the iframe and the iframe's parent document are Same Origin, returns a `Document` (that is, the active document in the inline frame's nested browsing context), else returns `null`.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLIFrameElement/contentDocument)contentDocument
? function readPayload(doc?: Document, id?: string): HydrationPayloadreadPayload(const iframe: HTMLIFrameElementiframe.HTMLIFrameElement.contentDocument: DocumentIf the iframe and the iframe's parent document are Same Origin, returns a `Document` (that is, the active document in the inline frame's nested browsing context), else returns `null`.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLIFrameElement/contentDocument)contentDocument, 'my-payload')
: null
@arrow-js/sandbox
sandbox()
Returns an ArrowTemplate that renders a stable
<arrow-sandbox> host element and boots a QuickJS +
WASM VM behind it.
Signature
import type { ArrowTemplate } from '@arrow-js/core'
type type HostBridgeFn = (...args: unknown[]) => unknown | Promise<unknown>HostBridgeFn = (...args: unknown[]args: unknown[]) => unknown | interface Promise<T>Represents the completion of an asynchronous operationPromise<unknown>
type type HostBridgeModule = {
[x: string]: HostBridgeFn;
}
HostBridgeModule = type Record<K extends keyof any, T> = { [P in K]: T; }Construct a type with a set of properties K of type TRecord<string, type HostBridgeFn = (...args: unknown[]) => unknown | Promise<unknown>HostBridgeFn>
type type HostBridge = {
[x: string]: HostBridgeModule;
}
HostBridge = type Record<K extends keyof any, T> = { [P in K]: T; }Construct a type with a set of properties K of type TRecord<string, type HostBridgeModule = {
[x: string]: HostBridgeFn;
}
HostBridgeModule>
interface SandboxProps {
SandboxProps.source: Record<string, string>source: type Record<K extends keyof any, T> = { [P in K]: T; }Construct a type with a set of properties K of type TRecord<string, string>
SandboxProps.shadowDOM?: booleanshadowDOM?: boolean;
SandboxProps.onError?: (error: Error | string) => voidonError?: (error: string | Errorerror: Error | string) => void;
SandboxProps.debug?: booleandebug?: boolean;
}
interface SandboxEvents {
SandboxEvents.output?: (payload: unknown) => voidoutput?: (payload: unknownpayload: unknown) => void;
}
declare function function sandbox<T extends {
source: object;
shadowDOM?: boolean;
onError?: (error: Error | string) => void;
debug?: boolean;
}>(props: T, events?: SandboxEvents, hostBridge?: HostBridge): ArrowTemplate
sandbox<function (type parameter) T in sandbox<T extends {
source: object;
shadowDOM?: boolean;
onError?: (error: Error | string) => void;
debug?: boolean;
}>(props: T, events?: SandboxEvents, hostBridge?: HostBridge): ArrowTemplate
T extends {
source: objectsource: object;
shadowDOM?: booleanshadowDOM?: boolean;
onError?: (error: Error | string) => voidonError?: (error: string | Errorerror: Error | string) => void;
debug?: booleandebug?: boolean;
}>(
props: T extends { source: object; shadowDOM?: boolean; onError?: (error: Error | string) => void; debug?: boolean; }props: function (type parameter) T in sandbox<T extends {
source: object;
shadowDOM?: boolean;
onError?: (error: Error | string) => void;
debug?: boolean;
}>(props: T, events?: SandboxEvents, hostBridge?: HostBridge): ArrowTemplate
T,
events: SandboxEventsevents?: SandboxEvents,
hostBridge: HostBridgehostBridge?: type HostBridge = {
[x: string]: HostBridgeModule;
}
HostBridge
): ArrowTemplate
Rules
-
sourcemust contain exactly onemain.tsormain.jsentry file. -
main.cssis optional and is injected into the sandbox host root. By default that root is an open shadow root. -
Pass
shadowDOM: falseto render into the custom element’s light DOM instead. -
Use the optional second argument to receive
output(payload)calls from inside the sandbox. - Use the optional third argument to expose host bridge modules that sandbox code can import directly.
Usage
import { function html(strings: TemplateStringsArray | string[], ...expressions: unknown[]): ArrowTemplatehtml } from '@arrow-js/core'
import { function sandbox<T extends {
source: object;
shadowDOM?: boolean;
onError?: (error: Error | string) => void;
debug?: boolean;
}>(props: T, events?: SandboxEvents, hostBridge?: HostBridge): ArrowTemplate
sandbox } from '@arrow-js/sandbox'
const const source: {
'main.ts': string;
}
source = {
'main.ts': [
"import { html, reactive } from '@arrow-js/core'",
"import { formatCount } from 'host-bridge:demo'",
'',
'const state = reactive({ count: 0 })',
'',
'export default html`<button @click="${() => state.count++}">',
' ${() => formatCount(state.count)}',
'</button>`',
].Array<string>.join(separator?: string): stringAdds all the elements of an array into a string, separated by the specified separator string.join('\n'),
}
function html(strings: TemplateStringsArray | string[], ...expressions: unknown[]): ArrowTemplatehtml`<main>${sandbox<{
source: {
'main.ts': string;
};
}>(props: {
source: {
'main.ts': string;
};
}, events?: SandboxEvents, hostBridge?: HostBridge): ArrowTemplate
sandbox({ source: {
'main.ts': string;
}
source }, {
SandboxEvents.output?: (payload: unknown) => voidoutput(payload: unknownpayload) {
var console: Consoleconsole.Console.log(...data: any[]): voidThe **`console.log()`** static method outputs a message to the console.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(payload: unknownpayload)
},
}, {
'host-bridge:demo': {
function formatCount(count: unknown): stringformatCount(count: unknowncount) {
return 'Count ' + count: unknowncount
},
},
})}</main>`
User-authored Arrow code runs inside QuickJS/WASM. The host page only mounts trusted DOM and forwards sanitized event payloads. It does not run user callbacks in the window realm.
Type Reference
All types are exported from their respective packages. Import them
with the type keyword for type-only imports.
@arrow-js/core
export type ParentNode = Node | DocumentFragment
export interface ArrowTemplate {
(parent: ParentNode): ParentNode
(): DocumentFragment
isT: boolean
key: (key: ArrowTemplateKey) => ArrowTemplate
id: (id: ArrowTemplateId) => ArrowTemplate
_c: () => Chunk
_k: ArrowTemplateKey
_i?: ArrowTemplateId
}
export type ArrowTemplateKey = string | number | undefined
type ArrowTemplateId = string | number | undefined
export type ArrowRenderable =
| string
| number
| boolean
| null
| undefined
| ComponentCall
| ArrowTemplate
| Array<string | number | boolean | ComponentCall | ArrowTemplate>
export type ArrowFunction = (...args: unknown[]) => ArrowRenderable
export type ArrowExpression =
| ArrowRenderable
| ArrowFunction
| EventListener
| ((evt: InputEvent) => void)
export type ReactiveTarget = Record<PropertyKey, unknown> | unknown[]
interface ReactiveAPI<T> {
$on: <P extends keyof T>(p: P, c: PropertyObserver<T[P]>) => void
$off: <P extends keyof T>(p: P, c: PropertyObserver<T[P]>) => void
}
export interface Computed<T> extends Readonly<Reactive<{ value: T }>> {}
type ReactiveValue<T> = T extends Computed<infer TValue>
? TValue
: T extends ReactiveTarget
? Reactive<T> | T
: T
export type Reactive<T extends ReactiveTarget> = {
[P in keyof T]: ReactiveValue<T[P]>
} & ReactiveAPI<T>
export interface PropertyObserver<T> {
(newValue?: T, oldValue?: T): void
}
export type Props<T extends ReactiveTarget> = {
[P in keyof T]: T[P] extends ReactiveTarget ? Props<T[P]> | T[P] : T[P]
}
export type EventMap = Record<string, unknown>
export type EventMap = Record<string, unknown>
export type Events<T extends EventMap> = {
[K in keyof T]?: (payload: T[K]) => void
}
export type Emit<T extends EventMap> = <K extends keyof T>(
event: K,
payload: T[K]
) => void
export type ComponentFactory = (
props?: Props<ReactiveTarget>,
emit?: Emit<EventMap>
) => ArrowTemplate
export interface AsyncComponentOptions<
TProps extends ReactiveTarget,
TValue,
TEvents extends EventMap = EventMap,
TSnapshot = TValue,
> {
fallback?: unknown
onError?: (
error: unknown,
props: Props<TProps>,
emit: Emit<TEvents>
) => unknown
render?: (
value: TValue,
props: Props<TProps>,
emit: Emit<TEvents>
) => unknown
serialize?: (
value: TValue,
props: Props<TProps>,
emit: Emit<TEvents>
) => TSnapshot
deserialize?: (snapshot: TSnapshot, props: Props<TProps>) => TValue
idPrefix?: string
}
export interface ComponentCall {
h: ComponentFactory
p: Props<ReactiveTarget> | undefined
e: Events<EventMap> | undefined
k: ArrowTemplateKey
key: (key: ArrowTemplateKey) => ComponentCall
}
export interface Component<TEvents extends EventMap = EventMap> {
(props?: undefined, events?: Events<TEvents>): ComponentCall
}
export interface ComponentWithProps<
T extends ReactiveTarget,
TEvents extends EventMap = EventMap,
> {
<S extends T>(props: S, events?: Events<TEvents>): ComponentCall
}
@arrow-js/framework
export interface RenderPayload {
RenderPayload.async: Record<string, unknown>async: type Record<K extends keyof any, T> = { [P in K]: T; }Construct a type with a set of properties K of type TRecord<string, unknown>
RenderPayload.boundaries: string[]boundaries: string[]
}
export interface RenderResult {
RenderResult.root: ParentNoderoot: ParentNode
RenderResult.template: ArrowTemplatetemplate: ArrowTemplate
RenderResult.payload: RenderPayloadpayload: RenderPayload
}
export interface BoundaryOptions {
BoundaryOptions.idPrefix?: stringidPrefix?: string
}
export interface DocumentRenderParts {
DocumentRenderParts.head?: stringhead?: string
DocumentRenderParts.html: stringhtml: string
DocumentRenderParts.payloadScript?: stringpayloadScript?: string
}
@arrow-js/ssr
export interface HydrationPayload {
HydrationPayload.html?: stringhtml?: string
HydrationPayload.rootId?: stringrootId?: string
HydrationPayload.async?: Record<string, unknown>async?: type Record<K extends keyof any, T> = { [P in K]: T; }Construct a type with a set of properties K of type TRecord<string, unknown>
HydrationPayload.boundaries?: string[]boundaries?: string[]
}
export interface SsrRenderOptions {
SsrRenderOptions.rootId?: stringrootId?: string
}
export interface SsrRenderResult {
SsrRenderResult.html: stringhtml: string
SsrRenderResult.payload: HydrationPayloadpayload: HydrationPayload
}
@arrow-js/hydrate
export interface HydrationMismatchDetails {
HydrationMismatchDetails.actual: stringactual: string
HydrationMismatchDetails.expected: stringexpected: string
HydrationMismatchDetails.mismatches: numbermismatches: number
HydrationMismatchDetails.repaired: booleanrepaired: boolean
HydrationMismatchDetails.boundaryFallbacks: numberboundaryFallbacks: number
}
export interface HydrationOptions {
HydrationOptions.onMismatch?: (details: HydrationMismatchDetails) => voidonMismatch?: (details: HydrationMismatchDetailsdetails: HydrationMismatchDetails) => void
}
export interface HydrationResult {
HydrationResult.root: ParentNoderoot: type ParentNode = Node | DocumentFragmentParentNode
HydrationResult.template: ArrowTemplatetemplate: ArrowTemplate
HydrationResult.payload: RenderPayloadpayload: RenderPayload
HydrationResult.adopted: booleanadopted: boolean
HydrationResult.mismatches: numbermismatches: number
HydrationResult.boundaryFallbacks: numberboundaryFallbacks: number
}
@arrow-js/sandbox
export interface SandboxProps {
source: Record<string, string>
shadowDOM?: boolean
onError?: (error: Error | string) => void
debug?: boolean
}
export interface SandboxEvents {
output?: (payload: unknown) => void
}
export function sandbox<T extends {
source: object
shadowDOM?: boolean
onError?: (error: Error | string) => void
debug?: boolean
}>(
props: T,
events?: SandboxEvents,
hostBridge?: HostBridge
): ArrowTemplate {
ensureSandboxElement()
return html`${SandboxHostComponent({
config: props as SandboxHostProps,
events,
hostBridge,
})}`
}