Skip to content

Components

When a piece of UI is ready to reuse, give it a name and a template. Components are a first-party feature: no extra plugin is needed.

Use a hyphenated custom-element name, an HTML template, and an optional setup function:

import { createApp } from '@jnreis/briojs';
const app = createApp({ state: () => ({ label: 'Clicks' }) });
app.component('click-counter', {
template: `
<button b-on-click="$data.count.value++; $data.notify()">
<span b-text="$props.label"></span>:
<span b-text="$data.count.value"></span>
<slot></slot>
</button>
`,
setup({ $props, $emit }) {
return {
count: { value: 0 },
notify() { $emit('change', $props.label); },
};
},
});
app.mount('#widgets');
<main id="widgets">
<click-counter b-bind-label="$data.label" b-on-change="console.log($event.detail)">
Tap to increment
</click-counter>
</main>
template valueUse
Markup stringKeep the HTML in your component definition.
A <template> elementKeep markup in the page, where editors highlight it.
An #id selector, such as '#tpl-click-counter'Find a <template> in the host’s document.

A selector that does not match a <template> throws when the first instance mounts.

Static host attributes become string props; b-bind-name supplies a reactive prop through $props. Declare props to coerce static attributes:

app.component('favorite-button', {
props: { compact: Boolean, count: Number },
template: '<button><span b-if="!$props.compact">Favorite </span><span b-text="$props.count"></span></button>',
});
<favorite-button compact count="3"></favorite-button>
Declared typeStatic attribute conversion
Booleanfalse when absent; true when present with any value except "false".
NumberPassed through Number().
String (default)Kept as a string.

Only these three types are accepted. Bound props (b-bind-*) are never coerced.

HTML lowercases attribute names: b-bind-userName arrives as $props.username. Use lowercase or dashed names ($props['user-name']).

Setup argumentValue
$props, $emitProps and component event emitter; also available in templates and contextual scripts.
$stores, $providedRegistered stores and inherited provided values.
$refThe component’s private DOM-ref registry.
el, appHost element and owning app.
routeThe router’s reactive route, when a router is installed.
  • Emit bubbling CustomEvents with $emit(name, detail).
  • Project named slots with slot="name" on content and <slot name="name"> in the template.
  • Setup runs in an owned scope. Effects, persistence, and listeners registered during setup are disposed when the component is removed.

Registered component tags do not use the ordinary-element directive pass. b-bind-* supplies props, not native host attribute/property bindings (so b-bind-class supplies a class prop; it does not style the host). Use b-attr-* to bind the host element itself, with the same behavior as b-bind-* on an ordinary element (class objects merge with static classes, style objects and custom properties, aria-*, data-*, booleans, and .prop). b-attr-* values are not passed as props:

<kpi-card class="card" b-bind-value="$for.kpi.value"
b-attr-class="{ rising: $for.kpi.delta > 0 }"
b-attr-data-testid="'kpi-' + $for.kpi.key"></kpi-card>

Each bound prop is evaluated once at mount, before setup, and again only when its dependencies change. Component template markup is parsed once per definition and cloned for each instance in the host’s own document. b-on-*/on-* installs host event listeners with the same event modifiers as ordinary elements (<row-actions b-on-remove.stop.once="…">, b-on-keydown.enter). Observer-style names are host event names, not observer installation; put observers on a concrete wrapper or inside the component template.

Earlier structural/context steps still apply: b-if, b-for with b-key, b-data, b-id, b-provide, and b-ref can be handled before component setup, subject to the combination rules above. b-ignore skips setup altogether, while b-template unwraps the tag instead of setting it up. b-transition animates the host itself, and b-attr-* binds the host’s own attributes. Other b-* host directives (such as b-model, b-html, and b-teleport) are rejected. Do not use unprefixed aliases to bypass that restriction; they do not install ordinary host behaviors either. Use a surviving wrapper or put the directive inside the component.