Provide / Inject Context API
Inspired by Svelte's Context API and Vue's Provide/Inject mechanism, this feature allows parent components to share state down the entire component tree without explicitly passing it down as props through each intermediate child.
§ Core Concepts
When designing related component groups (like forms, select lists, accordion panels, or tab pages), child components often need access to values or state managed by the parent container.
Instead of forcing developers to pass the parent's data to each nested field explicitly, the parent component can provide a context mapping, which child elements can then inject on-demand.
§ Form Context Example
Let's build a form validation container that automatically shares its name and error state with all child input controls nested within it.
1. The Parent: Provide Context
Create a form field container component at resources/views/components/field/root.nimbus. It registers a context key using $context.provide():
<!-- views/components/field/root.nimbus -->
{{
$context.provide('fieldContext', {
name: name,
id: id || name,
hasError: session.HasError(name),
errorMessage: session.Error(name)
})
}}
<div class="form-group mb-4">
{{{ .slots.main }}}
</div>
2. The Child: Inject Context
Create the input control at resources/views/components/input/control.nimbus. It accesses the provided properties using $context.inject():
<!-- views/components/input/control.nimbus -->
{{
const field = $context.inject('fieldContext')
}}
<input
name="{{ field.name }}"
id="{{ field.id }}"
class="input {{ field.hasError ? 'border-red-500' : 'border-slate-300' }}"
{{ $props.toAttrs() }}
/>
Similarly, write the label control at resources/views/components/field/label.nimbus to auto-associate the for="..." attribute:
<!-- views/components/field/label.nimbus -->
{{
const field = $context.inject('fieldContext')
}}
<label for="{{ field.id }}" class="block font-medium text-slate-700 mb-1">
{{{ .slots.main || text }}}
</label>
3. Consume the Components Together
Now, when building views, developers get automatic id-matching, name configuration, and error styling simply by nesting the tags:
@field.root({ name: 'email' })
@!field.label({ text: 'Email Address' })
@!input.control({ type: 'email', placeholder: 'you@example.com' })
@end
This compiles down into the following clean, error-aware markup without requiring the developer to pass name and id to both labels and inputs:
<div class="form-group mb-4">
<label for="email" class="block font-medium text-slate-700 mb-1">Email Address</label>
<input name="email" id="email" type="email" placeholder="you@example.com" class="input border-slate-300" />
</div>