https://github.com/stillat/dagger
A robust component authoring library for Laravel Blade.
https://github.com/stillat/dagger
blade laravel laravel-package
Last synced: 2 months ago
JSON representation
A robust component authoring library for Laravel Blade.
- Host: GitHub
- URL: https://github.com/stillat/dagger
- Owner: Stillat
- License: mit
- Created: 2025-01-20T05:08:14.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2025-02-27T01:31:29.000Z (3 months ago)
- Last Synced: 2025-04-05T05:01:47.167Z (2 months ago)
- Topics: blade, laravel, laravel-package
- Language: PHP
- Homepage:
- Size: 243 KB
- Stars: 144
- Watchers: 2
- Forks: 4
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE.md
Awesome Lists containing this project
README
# Dagger Components for Laravel Blade
Dagger is a component authoring library for Laravel's Blade templating engine. Dagger components are heavily inspired by Laravel's [anonymous components](https://laravel.com/docs/blade#anonymous-components). Dagger's differentiating features are its compiler and expanded capabilities.
The Dagger compiler works hard to inline your component's code, perform various optimizations, as well as enable powerful new features, such as the [Attribute Cache](#attribute-cache), [Attribute Forwarding](#attribute-forwarding), and [Slot Forwarding](#slot-forwarding). The end result is a powerful, performant component authoring library with a familiar syntax.
The main visual difference when working with Dagger components is the use of the `
@props(['type' => 'info', 'message'])
merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
``````blade
```
- [Frequently Asked Questions](#frequently-asked-questions)
- [Installation](#installation)
- [Dagger Component View Paths](#dagger-component-view-paths)
- [Index Components](#index-components)
- [Moving Dagger Component Views](#moving-dagger-component-views)
- [Component Syntax and the Component Builder](#component-syntax-and-the-component-builder)
- [Slots](#slots)
- [Named/Scoped Slots](#namedscoped-slots)
- [Custom PHP When Using the Component Function](#custom-php-when-using-the-component-function)
- [Calling Component Builder Methods](#calling-component-builder-methods)
- [Renaming the Component Variable](#renaming-the-component-variable)
- [Data Properties/Attributes](#data-properties--attributes)
- [Notes on Conditional aware and props Directives](#notes-on-conditional-aware-and-props-directives)
- [Accessing Parent Data](#accessing-parent-data)
- [Using the aware Directive](#using-the-aware-directive)
- [Using the aware Builder Method](#using-the-aware-builder-method)
- [Accessing Arbitrary Parent Data](#accessing-arbitrary-parent-data)
- [Aware Variables and Attributes](#aware-variables-and-attributes)
- [Property Validation](#property-validation)
- [Shorthand Validation Rules](#shorthand-validation-rules)
- [Compiler Attributes](#compiler-attributes)
- [Escaping Compiler Attributes](#escaping-compiler-attributes)
- [Caching Components](#caching-components)
- [Dynamic Cache Keys](#dynamic-cache-keys)
- [Specifying the Cache Store](#specifying-the-cache-store)
- [Stale While Revalidate/Flexible Caching](#stale-while-revalidate-flexible-cache)
- [Component Name](#component-name)
- [Component Depth](#component-depth)
- [Attribute Forwarding](#attribute-forwarding)
- [Nested Attribute Forwarding](#nested-attribute-forwarding)
- [Variable Bindings and Attribute Forwarding](#variable-bindings-and-attribute-forwarding)
- [Slot Forwarding](#slot-forwarding)
- [Nested Slot Forwarding](#nested-slot-forwarding)
- [Output Trimming](#output-trimming)
- [Stencils](#stencils)
- [Rendering Default Stencil Content](#rendering-default-stencil-content)
- [Additional Notes on Stencils](#additional-notes-on-stencils)
- [Mixins](#mixins)
- [Mixin Methods](#mixin-methods)
- [Accessing the Component Instance Inside Mixins](#accessing-the-component-instance-inside-mixins)
- [Additional Notes on Mixins](#additional-notes-on-mixins)
- [Attribute Cache](#attribute-cache)
- [Slot Variables and the Attribute Cache](#slot-variables-and-the-attribute-cache)
- [Considerations](#considerations)
- [Static Template Optimizations](#static-template-optimizations)
- [Dynamic Components](#dynamic-components)
- [Custom Component Paths and Namespaces](#custom-component-paths-and-namespaces)
- [Blade Component Prefix](#blade-component-prefix)
- [Compile Time Rendering](#compile-time-rendering)
- [Disabling Compile Time Rendering on a Component](#disabling-compile-time-rendering-on-a-component)
- [Enabling/Disabling Optimizations on Classes or Methods](#enablingdisabling-optimizations-on-classes-or-methods)
- [Notes on Compile Time Rendering](#notes-on-compile-time-rendering)
- [The View Manifest](#the-view-manifest)
- [License](#license)## Frequently Asked Questions
- [How does the Dagger compiler differ from Laravel's component compiler?](#how-does-the-dagger-compiler-differ-from-laravels-component-compiler)
- [Is an additional build step required?](#is-an-additional-build-step-required)
- [Are class-based components supported?](#are-class-based-components-supported)
- [Will this magically make my existing Blade components faster?](#will-this-magically-make-my-existing-blade-components-faster)
- [Can I use regular Blade components with Dagger components?](#can-i-use-regular-blade-components-with-dagger-components)
- [Why are there JSON files in my compiled view folder?](#why-are-there-json-files-in-my-compiled-view-folder)
- [Are circular component hierarchies supported?](#are-circular-component-hierarchies-supported)
- [Why build all of this?](#why-build-all-of-this)### How does the Dagger compiler differ from Laravel's component compiler?
The Dagger compiler is a multi-stage compiler that recursively parses and compiles a component's template ahead of time. Components compiled with the Dagger compiler will become *part of the view's* compiled output. Because of this, Laravel's related view events will *not* be fired when Dagger components are loaded.
### Is an additional build step required?
An additional build step is *not* required. Dagger components will be compiled the first time you load your views. [Dynamic Components](#dynamic-components) will be compiled automatically the first time they are encountered.
### Are class-based components supported?
Dagger only supports anonymous components, and there are no plans to support class-based components at this time. However, you may use [Mixins](#mixins) to gain back some of the benefits of class-based components when authoring Dagger components.
### Will this magically make my existing Blade components faster?
No. The Dagger compiler only interacts with components using one of the registered component prefixes (`
```Like with Blade's anonymous components, you may use the `.` character to indicate if a component is contained within a sub-directory. For a component defined at `resources/dagger/views/inputs/button.blade.php`, you may render it like so:
```blade
```
### Index Components
Dagger components follow the same rules as Laravel's [Anonymous Index Components](https://laravel.com/docs/blade#anonymous-index-components), allowing you to group components into their own self-contained directories.
Assuming the following component directory structure:
```text
/resources/dagger/views/accordion.blade.php
/resources/dagger/views/accordion/item.blade.php
```You could render the accordion component and the nested item like so:
```blade
...
```
If you'd prefer to not have the "root" view be located within the `/resources/dagger/views/` directory, you may create a file with the same name as the component within the component's directory:
```text
/resources/dagger/views/accordion/accordion.blade.php
/resources/dagger/views/accordion/item.blade.php
```Alternatively, you may also create a view named `index` within the component's directory:
```text
/resources/dagger/views/accordion/index.blade.php
/resources/dagger/views/accordion/item.blade.php
```### Moving Dagger Component Views
While it is *strongly discouraged* to move Dagger components into Laravel's `resources/views/components` directory, it is technically possible, and you are free to do what you want in your own project. If you'd like to move the Dagger component path, you may add the following to your applications service provider:
```php
```
The Dagger compiler supports Blade's `@props` and `@aware` directives, but also provides a new functional approach to defining components. When using the functional approach, the *first* thing within your component definition *must* be a PHP block, where the component is defined.
The following component definitions would produce identical output.
Using the component builder:
```blade
@php
use function Stillat\Dagger\component;component()->props(['type' => 'info', 'message']);
@endphpmerge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
```Using Blade directives:
```blade
@props(['type' => 'info', 'message'])
merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
```### Slots
Dagger slots behave the same as Blade's component slots. Default slot content is specified between component tag pairs. Assuming the following component definition:
```blade
{{ $slot }}
```Slot content may be specified like so:
```blade
The Slot Content
```
#### Named/Scoped Slots
Dagger components also support named or *scoped* slots. Accessing scoped slots is done through the `$slots` variable, which is different from Blade components. This is done to help prevent collisions with variables that may have the same name as desireable slot names.
Assuming the following component definition:
```blade
header->attributes }}>
{{ $slots->header }}{{ $slot }}
footer->attributes }}>
{{ $slots->footer }}
```You may specify content for each slot like so:
```blade
Header Content
Footer Content
Default Slot Content```
### Custom PHP When Using the Component Function
When using the functional syntax, it is important to note that PHP code that appears *before* the `component()` call may be removed from the compiled output. You can use this space to perform logic at compile time or set variables.
```blade
@php
use function Stillat\Dagger\component;// Danger zone.
$myCustomVariable = 'the value';component()->props(['type' => 'info', 'message']);
// Safe zone.
@endphpmerge(['class' => 'alert alert-'.$type]) }}>
{{ $message ?? $myCustomVariable }}
```To be safe, any custom PHP code you'd like to execute should appear after the `component()` call:
```blade
@php
use function Stillat\Dagger\component;component()->props(['type' => 'info', 'message']);
$myCustomVariable = 'the value';
@endphpmerge(['class' => 'alert alert-'.$type]) }}>
{{ $message ?? $myCustomVariable }}
```This does not apply to `use` statements and variable assignments. Any custom PHP code that appears before the `component()` call will be executed at *compile* time, however.
### Calling Component Builder Methods
When using the `component()` builder function, you should only call the `component()` function *once*. Do not make repeated calls to it:
```blade
@php
use function Stillat\Dagger\component;component()->props(['type' => 'info', 'message']);
component()->aware(['message']); ❌
@endphp...
```Instead, always chain the builder methods:
```blade
@php
use function Stillat\Dagger\component;component()->props(['type' => 'info', 'message'])
->aware(['message']); ✅
@endphp...
```### Renaming the Component Variable
If you'd like to rename the automatic `$component` variable within your component's definition, you may simply assign the results of the `component()` function to a variable:
```blade
@php
use function Stillat\Dagger\component;$theAlert = component()->props(['type' => 'info', 'message']);
@endphpmerge(['class' => 'alert alert-'.$theAlert->type]) }}>
{{ $theAlert->message }}
```> [!NOTE]
> The component instance will still be accessible via the `$component` variable inside slots, even if it has been renamed within the component itself. This is intentional to provide a consistent experience for component consumers.## Data Properties / Attributes
To ease development, and provide a familiar starting place, the Dagger compiler supports Blade's `@props` directive to help differentiate between which data is a *property* of the component, and what data should be placed inside the component's [attribute bag](https://laravel.com/docs/blade#component-attributes).
```blade
@props(['type' => 'info', 'message'])
merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
```Given the component above, we may render it like so:
```blade
```
We can also use the `props` component builder method to achieve the same results:
```blade
@php
use function Stillat\Dagger\component;component()->props(['type' => 'info', 'message']);
@endphpmerge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
```## Notes on Conditional aware and props Directives
Both the `@aware` and `@props` directives are supported by the Dagger compiler. However, because the compiler needs to know about their values ahead of time, conditional use of these directives is *not* supported in Dagger components.
You **must not** have Dagger component templates like the following:
```blade
@if ($someCondition)
@props(['title'])
@else
@props(['title', 'somethingElse'])
@aware(['title'])
@endif```
## Accessing Parent Data
You have multiple options for accessing parent data from within a child component.
- Using the aware directive
- Using the aware Builder Method
- Accessing Arbitrary Parent Data### Using the aware Directive
The most familiar option will be to use Blade's `@aware` directive.
Assuming we had a menu component with a parent `` and a child `` component:
```blade
...
...```
The `` component may have an implementation like the following:
```blade
@props([
'color' => 'gray'
])
- merge(['class' => 'bg-'.$color.'-200']) }}>
{{ $slot }}
```
Because the `color` prop was passed into the parent (``), it won't be immediately available inside ``. We can use the `@aware` directive to make that variable available inside `` as well:
```blade
@aware([
'color' => 'gray'
])
{{ $slot }}
```
### Using the aware Builder Method
Alternatively, we may also use the `aware` builder method to specify variables that should be passed to our child component:
```blade
@php
Stillat\Dagger\component()->aware(['color' => 'gray']);
@endphp
{{ $slot }}
```
### Accessing Arbitrary Parent Data
We can also access the parent component instance directly using the `parent()` method:
```blade
{{ $slot }}
```
If you are in a deeply nested component, you may access parent instances on parent instances:
```blade
{{ $component->parent()->parent()->someValue }}
```
You may also supply the name of the parent component you'd like to retrieve data from. Doing so will return access to the *nearest* parent instance with that name:
```blade
{{ $component->parent('nav')->someValue }}
```
### Aware Variables and Attributes
Aware variables will automatically be added to the component's props list, preventing them from appearing in the attribute bag.
```blade
@props(['color'])
{{ $slot}}
```
```blade
@aware(['color']) // "color" will be automatically added to the component's props.
```
## Property Validation
You may use Laravel's [validation](https://laravel.com/docs/validation) features to validate the *props* of your Dagger components. To do this, you may use the `validateProps` builder method to specify the prop and validation rules you'd like to enforce. As an example, the following would ensure that a `title` property was supplied to the `button` component:
```blade
@php
use function Stillat\Dagger\component;
component()
->props(['title'])
->validateProps([
'title' => 'required',
]);
@endphp
{{ $title }}
```
The following would not trigger a validation error:
```blade
```
while the following would:
```blade
```
The following data sources are also considered when validating components:
- Data made available via. the `aware` builder method or directive
- Data provided by mixins
### Shorthand Validation Rules
If you are using only string-based validation rules, you can skip the additional method call and specify them on the props directly. To do this, separate the prop name from the rules using the `|` character:
```blade
@php
use function Stillat\Dagger\component;
component()
->props(['title|required']);
@endphp
{{ $title }}
```
You can still specify default prop values when adding shorthand validation rules:
```blade
@php
use function Stillat\Dagger\component;
component()
->props([
'size|numeric|min:1|max:5' => 3,
]);
@endphp
The Size: {{ $size }}
```
## Compiler Attributes
The Dagger compiler introduces the concept of "compiler attributes", which have special meaning to the compiler. The most common of these is the `#id` attribute, which may be used to name a nested component:
```blade
```
Compiler attribute values **must** be static and **cannot** contain PHP expressions or reference variables.
### Escaping Compiler Attributes
If you need to output an attribute beginning with `#`, you may escape compiler attributes by prefixing it with another `#` character:
```blade
```
In general, you should avoid using props or attributes beginning with `#` as they are likely to be further developed upon and made available as an extension point, or may conflict with forwarded attributes. The following list of compiler attributes are currently in use, or are reserved for future internal use:
- `#id`
- `#name`
- `#compiler`
- `#style`
- `#def`
- `#group`
- `#styledef`
- `#classdef`
- `#cache`
- `#precompile`
## Caching Components
You may cache the output of any Dagger component using the `#cache` compiler attribute. This attribute utilizes Laravel's [Cache](https://laravel.com/docs/cache) feature, and provides ways to customize cache keys, expirations, and the cache store.
For example, imagine we had a report component that we'd like to cache:
```blade
@php
// Some expensive report logic.
@endphp
```
Instead of manually capturing output, or adding caching in other locations, we can simply cache the output of our component call like so:
```blade
```
Now, the output of the component will be cached forever using the `the-cache-key` string as the cache key.
We may also specify a different time-to-live by specifying the number of seconds the cached output is valid:
```blade
```
You may also use a shorthand notation to calculate the time-to-live in seconds. For example, if we'd like to have the cache expire ten minutes from the time the component was first rendered we could use the value `10m`:
```blade
```
Alternatively, we could also have the cache expire in 1 hour, 15 minutes, and 32 seconds:
```blade
```
The total number of seconds is calculated dynamically by adding the desired "date parts" to the current time and *then* calculating the number of seconds to use.
The following table lists the possible suffixes that may be used:
| Suffix | Description | Example |
|---|---|---|
| y | Year | 1y |
| mo | Month | 1mo |
| w | Week | 1w |
| d | Day | 2d |
| h | Hour | 1h |
| m | Minute | 30m |
| s | Seconds | 15s |
### Dynamic Cache Keys
You may create dynamic cache keys by prefixing the `#cache` attribute with the `:` character:
```blade
id"
/>
```
### Specifying the Cache Store
You may use a specific cache store by providing the desired cache store's name as the final modifier to the `#cache` attribute.
The following examples would cache the output for 30 seconds on different cache stores:
```blade
```
### Stale While Revalidate (Flexible Cache)
You may leverage Laravel's [stale-while-revalidate pattern implementation](https://laravel.com/docs/11.x/cache#swr) using the `flexible` cache modifier. This modifier accepts two values: the number of seconds the cache is considered fresh, and the second value determines how long the cached contents can be served as stale before recalculation is necessary.
```blade
```
## Component Name
You may access the name of the current component through the `name` property on the component instance:
```blade
{{-- Displays "button" --}}
{{ $component->name }}
```
## Component Depth
You may get the current depth of the current component using the `depth` property on the component instance:
```blade
{{ $component->depth }}
```
Each time a component is nested, the depth is increased by one. Depth is also incremented when the parent component is a Blade component.
## Attribute Forwarding
Attribute forwarding is a powerful feature that allows you to set and override props and attributes on *nested* components. For this to work, nested components must have an identifier, which is set using the `#id` compiler attribute.
Imagine we have the following simple toolbar component:
```blade
```
and the following button component:
```blade
@php
\Stillat\Dagger\component()
->props(['text'])
->trimOutput();
@endphp
{{ $text }}
```
If we were to render the following template:
```blade
```
we would receive output similar to the following:
```html
The Save Button
The Cancel Button
```
If we wanted to allow consumers of the `toolbar` component to modify both the cancel and save buttons, we historically would have to create dedicated props on the parent and pass the values to each child component or define extra slots and pass in our nested components. However, because each of the nested button components has an `#id`, we can use attribute forwarding to set props and attributes on the nested components.
If we adjusted our *template* to the following:
```blade
```
we would now get output similar to the following:
```html
A New Save Button
A New Cancel Button
```
When using attribute forwarding we specify the `#id` of the nested component followed by the `:` character, and then the name of the prop or attribute to update.
### Nested Attribute Forwarding
Attributes and props can be forwarded to nested components. To do so, we separate each nested component name with the `.` character. Imagine we had the following components:
```blade
```
```blade
```
```blade
@props(['title'])
{{ $title }}
```
We can set the `title` prop on the nested `` component from the template using attribute forwarding like so:
```blade
```
### Variable Bindings and Attribute Forwarding
You may pass in variable references when using attribute forwarding by prefixing the forwarded attribute with the `:` character:
```blade
```
## Slot Forwarding
We may specify slot contents on *nested* components. To do so, the nested components must have an identifier specified using the `#id` compiler attribute. Imagine we have the following components:
```blade
```
```blade
{{ $slots->header }}
{{ $slot }}
{{ $slots->footer }}
```
We could specify the slot contents on the nested `componentOne` within our template using slot forwarding:
```blade
Nested Header Content
Nested Footer Content
Nested Default Content
```
We do not use the `#` symbol when referencing nested slots, and instead separate the nested path using the `.` character. You will also notice that we were able to specify the *default* slot content using the `.default` name.
### Nested Slot Forwarding
We may also set the contents of deeply nested components. To do so, we continue to add the names of nested components, separated by the `.` character. Consider the following components:
```blade
```
```blade
```
```blade
{{ $slots->header }}
{{ $slot }}
{{ $slots->footer }}
```
we could set the nested slot contents like so:
```blade
Nested Header Content
Nested Footer Content
Nested Default Content
```
## Output Trimming
There are times you may wish to trim the output of a component before it is rendered on the client. Instead of manually capturing output, or carefully ensuring that each component file does not contain a final newline, you can instead use the `trimOutput` builder method:
```blade
@php
\Stillat\Dagger\component()
->props(['name'])
->trimOutput();
@endphp
{{ $name }}
```
The Dagger compile will now trim the output of the component before adding it to the rest of the response.
> [!NOTE]
> Any leading content, such as HTML comments, before the first `@php ... @endphp` block will be considered content when trimming component output.
## Stencils
Stencils allow consumers of components to override named sections of a component *without* having to publish the component's views. They are similar to slots, but work in a very different way. Slots are a runtime feature, where content is evaluated within the consumer's variable scope and the results are injected in the component as a variable. Stencils, on the other hand, are a *compile time* substitution and become part of the component's compiled output.
Stencils, by themselves, simply create a "named" section of a component's template that the compiler can replace. These regions are created using the special `` component. Imagine we had the following list component:
```blade
@props(['items'])
- {{ $item }}
@foreach ($items as $item)
@endforeach
```
If we were to render the component like so:
```blade
```
Our output would resemble the following:
```html
- Alice
- Bob
- Charlie
```
However, because the component defined a `list_item` stencil, we can replace that section of the component's template entirely:
```blade
```
Rendering the new template would produce output similar to the following:
```html
- ALICE
- BOB
- CHARLIE
```
### Rendering Default Stencil Content
There may be times where you'd like to change a stencil's template, but conditionally render the original. Building on the list example above, we can accomplish this by using a special `default` modifier provided by the stencil component:
```blade
@if ($item === 'Alice')
@else
@endif
```
Rendering this template would now produce the following output:
```html
- Alice
- Bob
- Charlie
```
### Additional Notes on Stencils
A few things to remember when using stencils:
- Stencils do *not* have access to the consumer's scope
- Stencil templates become part of the component's compiled output and have access to the component's internal scope
- Default stencil templates can be injected using the `` component, where `stencil_name` is the name of the stencil
- Stencils, by themselves, have no additional overhead once compiled
## Mixins
Mixins provide a way to inject data and common behaviors into components. Mixins are specified by calling the `mixin` component builder method and supplying either a single class name, or multiple mixin class names.
Mixin classes may define a `data` method, which should return an array of key/value pairs. Imagine we had the following mixin class providing common theme data:
```php
'bg-indigo-500',
];
}
}
```
We could include this mixin in our component like so:
```blade
@php
\Stillat\Dagger\component()->mixin([
\App\Mixins\ThemeData::class,
])->props(['background']);
@endphp
...
```
Data returned by mixins will be injected as variables, like regular props. Prop values will override any values provided by a mixin:
```blade
```
A mixin's `data` method will be invoked *last* when registering the mixin with the component.
### Mixin Methods
Public methods defined within a mixin will be injected as variables within the component's view:
```php
@php
\Stillat\Dagger\component()->mixin([
\App\Mixins\ProfileMixin::class,
])->props(['name']);
@endphp
{{ $sayHello($name) }}
```
If you prefer not to use variables as methods, you may also access mixin methods on the `$component` instance. This is also helpful if you have props that share the same name as methods:
```blade
@php
\Stillat\Dagger\component()->mixin([
\App\Mixins\ProfileMixin::class,
])->props(['name']);
@endphp
{{ $component->sayHello($name) }}
```
### Accessing the Component Instance Inside Mixins
You can gain access to the `$component` instance within a mixin class by defining a `withComponent` method that accepts the component as its only argument. The `withComponent` method will be invoked *first* if it exists:
```php
component = $component;
}
public function data(): array
{
return [
'name_upper' => Str::upper($this->component->name),
];
}
}
```
```blade
@php
\Stillat\Dagger\component()->mixin([
\App\Mixins\ComponentMixin::class,
]);
@endphp
{{ $name_upper }}
```
### Additional Notes on Mixins
* Mixin instances are resolved from the service container *each* time a component is used
* The `withComponent` method is always called first, if present
* The `data` method is always called last, if present
* Data provided to mixins via. `data` methods will *not* become part of the `$attributes`, even if they are not listed in the props
* Public methods defined within a mixin will be made available as variables within the component
* The `withComponent` and `data` methods will *not* be made available
## Attribute Cache
The Dagger compiler and runtime provide an opt-in feature called the attribute cache. This cache mechanism is able to cache the results of components, while still allowing for dynamic slot substition. The attribute cache may be used to prevent re-rendering components when the same attributes are supplied to it, helping to improve performance for heavy, large, or complicated components.
To use the attribute cache simply call the `cache` component builder method:
```blade
@php
\Stillat\Dagger\component()
->props(['title'])
->cache();
@endphp
{{ $title }}
```
Assuming the following template, the `` component internals would only be evaulated once because the attributes and props remain the same across both renderings:
```blade
```
However, the `` component would be evaluated *twice* in the following template:
```blade
```
Any attributes supplied on named/scoped slots will also be added to the internal cache key. All instances of a component will share the same internal attribute cache.
### Slot Variables and the Attribute Cache
Components with slots are still able to take advantage of the attribute cache. Internally, the Dagger compiler will perform string substition on the cached component output. Because of this behavior, you should avoid performing operations on the *results* of slots when enabling the attribute cache:
```blade
@php
\Stillat\Dagger\component()
->props(['title'])
->cache();
@endphp
{{ Str::upper($slot) }} // ❌ Don't change the output of slots when using the attribute cache.
```
### Considerations
The attribute cache is a powerful feature that can help to improve performance for heavily re-used components, but there are some things to keep in mind before deciding to use it. Any custom PHP code or dynamic behavior within your component will only be evaluated for each unique rendering of the component.
Consider the following component:
```blade
@php
\Stillat\Dagger\component()
->cache();
@endphp
{{ time() }}
```
The results of the `time()` function call would be the same for all of the following renders, since it was cached:
```blade
```
If you're component's internal execution needs to remain dynamic you *should not* use the attribute cache. Because the Dagger compiler inlines components, performance should remain relatively stable in these scenarios, even for heavily-used components.
## Static Template Optimizations
If a component's template does *not* contain any PHP code after compilation, the Dagger compiler will **not** output any component infrastructure for that component. Instead, it will simply inline the static component's content in the view's compiled output. Dagger maintains a "view manifest" which tracks components like this, allowing things like hot reloading and cache busting to continue functioning.
Imagine we had a footer component that contained static HTML contents:
```html
...
```
Rendering the `` component would not output any additional PHP code in the final, compiled output.
## Dynamic Components
If you need to render dynamic Dagger components, you may use the `dynamic-component` component to render a component based on a runtime value or variable:
```blade
// $componentName = "button";
```
You may also supply slot content to dynamic components:
```blade
The Slot Contents
```
Dynamic components will be compiled and cached, taking into account any slots, forwarded attributes, or forwarded slots. Dynamic Dagger components can also take advantage of the Attribute Cache.
You may use any of the following aliases to render dynamic components:
```blade
```
All three aliases share the same internal behavior.
## Custom Component Paths and Namespaces
If you are writing a component library, you may wish to register your own component namespace and not have to rely on the `
```
Custom components can leverage all features of the Dagger compiler using their custom prefix.
### Blade Component Prefix
You are **not** allowed to register the prefix `x` with the Dagger compiler; attempting to do so will raise an `InvalidArgumentException`.
## Compile Time Rendering
The Dagger compiler contains a subsystem known as the Compile Time Renderer, or CTR. This checks to see if all the props on a component are resolvable at runtime; if so, it may elect to compile the component at runtime and insert the pre-rendered results into Blade's compiled output.
This feature has a number of internal safe guards, and here are a few of the things that will internally disable this feature:
- Dynamic/interpolated variable references
- Using Mixins
- Most static method calls
- Referencing PHP's [superglobals](https://php.net/superglobals), such as `$_GET` or `$_POST`
- Using debugging-related functions in a component, such as `dd`, `dump`, `var_dump`, etc.
- Calling functions such as `time`, `now`, or `date`
- Enabling the Attribute Cache on a component
- Components with slots
Imagine we have the following alert component:
```blade
@props(['type' => 'info', 'message'])
{{ $message }}
```
If we were to call the alert component like so:
```blade
```
The compiler would detect that all props are resolvable, and the following would be emitted in the compiled Blade output:
```html
The awesome message
```
However, if we were to call our component like so, the compiler would not attempt to render the component at compile time:
```blade
```
### Disabling Compile Time Rendering on a Component
The CTR system should be transparent from a component author's point-of-view, however, if the rare event that you need to disable compiler optimizations, you may do so using the `compiler` helper method:
```blade
@php
\Stillat\Dagger\component()
->props(['title'])
->compiler(
allowOptimizations: false
);
@endphp
{{ $title }}
```
If you find yourself disabling optimizations on a component, please open a discussion or an issue with details on which behaviors led to that decision.
### Enabling/Disabling Optimizations on Classes or Methods
The CTR system will aggressively disable itself whenever it detects static method calls within component templates. You may choose to mark these methods as safe using the `EnableOptimization` attribute:
```php