Components
This guide documents how Regor components work in runtime, based on current implementation and tests.
Define a Component
Section titled “Define a Component”import { defineComponent, html } from 'regor'
export const UserCard = defineComponent( html`<article><h3 r-text="title"></h3></article>`, { props: ['title'] },)defineComponent(template, options) supports:
- Template from string:
defineComponent('<div>...</div>') - Template object with
template - Template object with
element - Template object with
selector - Template object with
json
Interpolation is enabled by default (useInterpolation: true).
Register and Use Components
Section titled “Register and Use Components”Register via app context:
createApp({ components: { UserCard },})or via RegorConfig.addComponent(...) (registered/global style).
Tags resolve in component map using component name and kebab-case variant.
<UserCard :title="activeUser.name" /> <user-card :title="activeUser.name" />Component Context and ComponentHead
Section titled “Component Context and ComponentHead”Component context is created by options.context(head).
head is ComponentHead and exposes:
head.props: resolved incoming props object.head.emit(event, detail): dispatchesCustomEventfrom host element.head.autoProps(defaulttrue): auto-assigns props into component context fields.head.entangle(defaulttrue): if both sides are refs, parent and component refs are two-way entangled during auto-props.head.enableSwitch(defaultfalse): enables slot context switching to parent for slot templates.head.onAutoPropsAssigned: callback after auto props assignment.head.findContext(ContextClass, occurrence?): returns matching parent context instance fromhead.ctxbyinstanceof, orundefined.head.requireContext(ContextClass, occurrence?): resolves matching parent context instance fromhead.ctxbyinstanceof; throws if the selected occurrence does not exist.head.unmount(): removes mounted nodes in component range and calls unmounted hooks.
occurrence is zero-based:
0(default): first match1: second match2: third match
Emit Example
Section titled “Emit Example”class CardContext { title = ref('local') $emit?: (event: string, args: Record<string, unknown>) => void save = () => this.$emit?.('save', { title: this.title() })}
const Card = defineComponent('<button @click="save">Save</button>', { context: () => new CardContext(),})In parent:
<Card @save="onSave($event)" />Parent context lookup example
Section titled “Parent context lookup example”class AppServices { api = '/v1'}
class OuterLayoutContext {}
const Child = defineComponent('<div></div>', { context: (head) => { const services = head.requireContext(AppServices) const secondLayout = head.findContext(OuterLayoutContext, 1) return { services, secondLayout } },})How Component Inputs Are Routed
Section titled “How Component Inputs Are Routed”On a component host tag, Regor routes bindings through two different channels:
- Component input channel: values end up in
head.propsand can become component state. - Attribute fallthrough channel: values are copied as DOM attributes/class/style to rendered output root.
1) Declared single-prop bindings (component input channel)
Section titled “1) Declared single-prop bindings (component input channel)”Use:
:x="...".x="..."r-bind:x="..."
These are treated as component props only if x is declared in props: [...].
const Card = defineComponent('<h3 r-text="title"></h3>', { props: ['title'], context: (head) => ({ title: head.props.title }),})<Card :title="user.name"></Card> <Card r-bind:title="user.name"></Card>If x is not declared in props, that single binding is treated as normal attribute fallthrough.
2) Object component input (:context / r-context)
Section titled “2) Object component input (:context / r-context)”Use:
<Card :context="{ title: user.name, badge: role }"></Card><Card r-context="{ title: user.name, badge: role }"></Card>This is object-style component input and does not require keys to be listed in props.
3) Object-form r-bind uses the attribute channel
Section titled “3) Object-form r-bind uses the attribute channel”<Card r-bind="{ id: cardId, 'data-role': role }"></Card>For component hosts, this form is processed as attribute forwarding to rendered output nodes.
It does not go through declared-prop resolution or :context object assignment.
r-bind:x="..." is different: it is single-key binding and can map to component input when x is declared in props.
autoProps and entangle behavior
Section titled “autoProps and entangle behavior”head.autoProps = false: component author maps fromhead.propsmanually.head.autoProps = true+head.entangle = true: ref-to-ref inputs are two-way entangled.head.autoProps = true+head.entangle = false: ref-to-ref inputs are initial snapshot only.- Primitive/object values targeting existing component ref fields are applied to those refs.
Supported slot patterns:
- Default slot:
<slot></slot> - Named slot:
<slot name="extra"></slot> - Named slot shorthand in component template:
<slot #extra></slot> - Host-side named template:
<template name="extra">...</template>or<template #extra>...</template> - Fallback slot content when host does not provide matching content.
Default slot behavior:
- If host provides unnamed content, it is used.
- Named-only template shortcuts are not injected into default slot.
For parent-context slot expression switching, enable:
context: (head) => { head.enableSwitch = true return {}}Attribute inheritance (inheritAttrs)
Section titled “Attribute inheritance (inheritAttrs)”By default, component host attributes are inherited into component output root (inheritAttrs: true).
Details:
classis merged.styleproperties are merged.- Other attributes are copied.
:contextis excluded from inherit copy.- If component has multiple root elements,
r-inheritcan mark intended inheritor.
Set inheritAttrs: false to disable this behavior.
Dynamic components with :is
Section titled “Dynamic components with :is”<div :is="currentCard" :item="item"></div>Switching :is remounts target component selection while keeping reactive prop flow behavior.
Nested components and lifecycle
Section titled “Nested components and lifecycle”Nested component trees are supported (r-for, r-if, nested slots).
Component lifecycle hooks run through standard mounted/unmounted flow.
Unmount cleans child component bindings and observers.
Best Practices
Section titled “Best Practices”- Declare single props in
propswhen using individual bindings. - Use
:contextfor object-style prop passing. - Use
head.enableSwitch = truewhen slot content must evaluate in parent context. - Decide
autoProps+entangleexplicitly when designing parent-child data ownership. - Keep fallback slot content for robust component defaults.
- Use stable keys for component lists rendered with
r-for.