Nesting, islands & lazy
Brew components nest like any other Crema tag. Children keep their own snapshots; the parent records them under memo.children so later requests can skip re-rendering unchanged children.
Nesting a child
Section titled “Nesting a child”{{-- parent template --}}<div> <brew:panel wire:key="main" title="Main" class="card" /> <brew:dynamic-component :is="childName" /></div>wire:key/keygives a stable identity in loops.- Extra HTML attributes that are not props are forwarded onto the child’s root as
data-brew-attr-*and exposed as anattributesbag in the view. <brew:dynamic-component :is="…" />(or:component) picks the child name at render time.- A component may nest itself — stop the recursion with a depth (or similar) guard in
template().
Reactive & modelable props
Section titled “Reactive & modelable props”class Panel : Component() { @field:Reactive var title: String = ""
@field:Modelable var value: String = ""}public class Panel extends Component { @Reactive public String title = ""; @Modelable public String value = "";}@Reactive— parent re-pushes the prop whenever the parent re-renders (memo.props).@Modelable— marks the property the parent binds withwire:modelon the child tag (memo.bindings).
<brew:card> <brew:slot name="footer">Save</brew:slot> Body content</brew:card>Inside the child:
<div> {{ slots.get('default') }} @if(slots.has('footer')) <footer>{{ slots.get('footer') }}</footer> @endif</div>slots is a SlotBag with has, get, default(), and all().
Listening for child events
Section titled “Listening for child events”Put @event-name="method" on the child tag. When the child dispatches that event, Brew calls the parent’s method:
<brew:panel @saved="onPanelSaved" />fun onPanelSaved(label: String = "") { heard = label }$parent
Section titled “$parent”From JS: $wire.$parent.save(). From the server / wire:click: $parent magic action forwards to the parent component ($parent, then the method name and args).
Inside Kotlin/Java: parent() returns the enclosing component while nested (also available after mount via the stored parent reference).
Lazy & defer
Section titled “Lazy & defer”@Lazy(bundle = true)class Heavy : Component() { override fun placeholder() = "<em>Loading…</em>"}@Lazy/lazymount param — placeholder until the element intersects the viewport.@Defer/defer— load after the first paint (microtask).bundle = true— group lazy loads (memo.lazyBundle).@placeholder('…')/BrewConfig.lazyPlaceholder— default markup.Brew.withoutLazyLoading { … }— mount eagerly in tests.
Islands
Section titled “Islands”@island('stats') <p>{{ count }}</p>@endisland
<button wire:click.island.stats.append="add">Add</button>- Named or anonymous (
@island→default). - Meta flags in the expression:
lazy,defer,always,skip. - JS:
$wire.$island('stats', 'append'|'prepend'|'morph')and the.islandclick modifier. - Polling inside an island uses the same
wire:polldirective as anywhere else.
Teleport
Section titled “Teleport”@teleport('body') <aside>Portaled</aside>@endteleportRenders a <template wire:teleport="selector">; the JS runtime moves the contents to document.querySelector(selector).
Isolate
Section titled “Isolate”@Isolateclass ChatPane : Component()Isolated children skip request bundling with their siblings (memo.isolate).
- Components — registration and discovery
- Events —
dispatch,@On, JS listeners - JavaScript —
$wireAPI