Extending Alpine.js components

Extending existing Alpine.js components can be done in various ways. We'll go through this in this document.

Creating a new Alpine.js component

The regular way of extending Alpine.js components is to create your own new component and extend from the available component types. For instance, you could create a new block via the XML layout as follows:

<referenceBlock name="loki.script.component">
    <block
            name="yireo-training-example.script.components.example"
            template="YireoTraining_Example::script/component/example.phtml"/>
</referenceBlock>

Within the PHTML template, you could then extend upon

<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data('ExampleComponent', () => ({
            ...LokiComponentType,
            initHelloWorld() {
                console.log('Hello Example Component);
            }
        }));
    });
</script>

Finally, you could define some other block with template as a Loki Component with the component name being ExampleComponent, as discussed elswhere.

The upside of this approach is that you are creating a child component in a clean way, without conflicting with other components. The downside is that this approach requires a new component. What about modifying existing components?

Mixing your own data into existing components

The LokiComponentExtender object allows for a neat way to extend upon existing Alpine.js components. Actually, it replaces the original Alpine.data() factory and replaces it with support for LokiComponentExtender.extensions callbacks.

To use this, first, create a new script for your specific mixin:

<referenceBlock name="loki-components.script.component-extender">
	<block name="example" template="YireoTraining_Example::script/mixin/example.phtml" />
</referenceBlock>

Next, within the PHTML template, define a new mixin via the LokiComponentExtender.add() method: The first argument is an identifier for your mixin. The second argument is a callback that should be called whenever Alpine.data() is run. In this case, we return an object with a method initHelloWorld(). And thanks to the fact that - with Loki Components - any method starting with init is automatically run as part of the Alpine.js init() procedure, the console log is printed. Note that this runs on any Loki Components.

<script>
    LokiComponentExtender.add('ExampleMixin', () => {
        return {
            initHelloWorld() {
                console.log('Hello', this.id);
            }
        }
    });
</script>

Mixin for one specific component name

What about adding this to a specific component only? That's easy: The callback (the second argument to LokiComponentExtender.add()) receives a componentName. In the example below, the mixin is only applied if the component name is LokiFieldComponent (so: x-data="LokiFieldComponent").

<script>
    LokiComponentExtender.add('ExampleMixin', (componentName) => {
        if (componentName !== 'LokiFieldComponent') {
            return false;
        }

        return {
            initHelloWorld() {
                console.log('Hello', this.id);
            }
        }
    });
</script>

A shorter syntax for the same thing runs via the addMixin() method:

<script>
    LokiComponentExtender.addMixin('ExampleMixin', 'LokiFieldComponent', {
        initHelloWorld() {
            console.log('Hello', this.id);
        }
    });
</script>

Mixin for one specific component title or ID

Within Loki Components, a component like LokiFieldComponent could be used multiple times. (Because of this, we go through the trouble of talking about component partials, component types, components and component instances.) Every instance, however, has its own uniq title (x-data="Foobar") and ID (equal to the block name and element ID). Unfortunately, these are determined at run time, so you can't check for them with the LokiComponentExtender.

Last modified: October 20, 2025