Directive-based Alpine.js components with Shadow DOM encapsulation, slots, and cached template rendering.
v2 is directive-based and built around x-component.
- No custom element registration required
- Supports on-page templates and remote templates
- Renders into Shadow DOM for style encapsulation
- Supports default and named slots from host templates
- Emits lifecycle events for loading, loaded, and error states
- Uses bounded caches for templates, remote responses, and stylesheets
<script
defer
src="https://unpkg.com/alpinejs-component@latest/dist/component.min.js"
></script>
<script defer src="https://unpkg.com/alpinejs@latest/dist/cdn.min.js"></script>npm install alpinejs-component
yarn add alpinejs-component
pnpm add alpinejs-componentimport Alpine from 'alpinejs'
import component from 'alpinejs-component'
Alpine.plugin(component)
Alpine.start()v2 uses an Alpine directive: x-component.
x-component="expression": render from an on-page<template id="...">x-component.url="expression": render from a URLx-component.url.external="expression": allow cross-originhttp(s)URLsx-component-styles="title-a,title-b": include matching document stylesheetsstyles="...": alias forx-component-styles
The directive expression can be static or dynamic. Values are normalized as:
string: trimmed and used directlynumber/boolean/ other primitives: converted withString(...)null/undefined/ empty string: treated as empty source
When the resolved source is empty, the mounted component is unmounted/cleared.
<div
x-data="{
people: [
{ name: 'John', age: '25', skills: ['JavaScript', 'CSS'] },
{ name: 'Jane', age: '30', skills: ['Laravel', 'MySQL', 'jQuery'] }
]
}"
>
<ul>
<template x-for="person in people" :key="person.name">
<li>
<div x-data="{ item: person }" x-component="'person-card'"></div>
</li>
</template>
</ul>
</div>
<template id="person-card">
<article>
<h2 x-text="item.name"></h2>
<p x-text="item.age"></p>
<ul>
<template x-for="skill in item.skills" :key="skill">
<li x-text="skill"></li>
</template>
</ul>
</article>
</template>Use the .url modifier when the expression resolves to a URL.
By default, .url only allows http(s) URLs on the current origin. Add the
.external modifier to allow cross-origin http(s) URLs.
<div
x-data="{
people: [
{ name: 'John', age: '25', skills: ['JavaScript', 'CSS'] },
{ name: 'Jane', age: '30', skills: ['Laravel', 'MySQL', 'jQuery'] }
]
}"
>
<ul>
<template x-for="person in people" :key="person.name">
<li>
<div
x-data="{ item: person }"
x-component.url="'/public/person-card.html'"
></div>
</li>
</template>
</ul>
</div>x-component and x-component.url support dynamic expressions.
<div
x-data="{
view: 'person-card',
remoteView: '/public/person-card.html'
}"
>
<section x-component="view"></section>
<section x-component.url="remoteView"></section>
</div>Rendered component content is mounted in a Shadow DOM root.
Use x-component-styles (or styles) to include selected document stylesheets
by title.
<style title="person-card">
article {
border: 1px solid #ddd;
}
</style>
<div x-component="'person-card'" x-component-styles="person-card"></div>Use global to include all local stylesheets:
<div x-component="'person-card'" x-component-styles="global"></div>Slot templates can be declared on the host element with x-slot.
<div x-component="'card-with-slot'">
<template x-slot>
<p>Default slot content</p>
</template>
<template x-slot="actions">
<button>Save</button>
</template>
</div>
<template id="card-with-slot">
<article>
<slot></slot>
<footer>
<slot name="actions"></slot>
</footer>
</article>
</template>The host element emits lifecycle events:
x-component:loadingwhen URL loading startsx-component:loadedwhen render completesx-component:errorwhen expression evaluation, loading, or rendering fails
Event detail payloads:
x-component:loading:{ source }x-component:loaded:{ source }x-component:error:{ source, error }
source is the resolved template id/URL for load/render failures, and the raw
directive expression for expression-evaluation failures.
Evaluation failure behavior:
- If directive expression evaluation throws, the plugin emits
x-component:errorwith the evaluation error. - The component source is treated as empty, so any currently mounted content is cleared.
<div
x-component.url="'/public/person-card.html'"
x-on:x-component:loaded="console.log('component ready', $event.detail)"
x-on:x-component:error="console.error('component failed', $event.detail)"
></div>Important: Only load templates from trusted sources. This plugin:
- Renders HTML content directly (no sanitization)
- Performs minimal URL validation (only
http(s)and same-origin by default) - Is designed for developer-controlled content
Your responsibility:
- Don't use user input directly in
x-componentorx-component.url - Only load templates from your own trusted servers
- Validate/sanitize any dynamic template selection
- Use CSP headers for additional protection
x-component.urlaccepts onlyhttp(s)URLsx-component.urlblocks cross-origin requests by default- Use
x-component.url.externalto opt into cross-originhttp(s)requests
This plugin targets modern browsers with support for:
- Shadow DOM
adoptedStyleSheets(when usingx-component-styles/styles)CSSStyleSheet(when usingx-component-styles/styles)template.content
If your target environment lacks these APIs, use a compatibility strategy or avoid Shadow DOM style adoption features.
The plugin maintains bounded in-memory caches:
- Template fragments by template id (limit: 200)
- Remote template fetch promises by normalized URL (limit: 200)
- Adopted stylesheets by style target list (limit: 100)
When a cache exceeds its limit, oldest entries are evicted.
For URL mode, failed fetches are removed from cache so retries can succeed.
Use pnpm. There is no npm lockfile.
pnpm install
pnpm buildAvailable scripts:
pnpm build: lint then build minified CDN + ESM outputs indist/pnpm lint: run ESLint with--fixpnpm format: run Prettier over the repo
- Missing templates and failed URL requests are handled with console warnings/errors and lifecycle error events.
- Expression evaluation failures dispatch
x-component:errorand clear mounted content for that host. - URL responses are cached by URL.
- Template fragments are cached by template id.
- Stylesheets are cached by style target list.
v1:
<x-component template="person"></x-component>
<x-component url="/public/person.html"></x-component>v2:
<div x-component="'person'"></div>
<div x-component.url="'/public/person.html'"></div>window.xComponent.name custom-element renaming is no longer used because v2 is
directive-based.