Skip to content

markmead/alpinejs-component

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

28 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Alpine JS Component

Reusable HTML components powered by Alpine JS reactivity.

โœจ Demos: https://alpinejs-component-demo.vercel.app/.

V2 Overview

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

Install

With a CDN

<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>

With a Package Manager

npm install alpinejs-component
yarn add alpinejs-component
pnpm add alpinejs-component
import Alpine from 'alpinejs'
import component from 'alpinejs-component'

Alpine.plugin(component)
Alpine.start()

Usage

v2 uses an Alpine directive: x-component.

Directive Reference

  • x-component="expression": render from an on-page <template id="...">
  • x-component.url="expression": render from a URL
  • x-component.url.external="expression": allow cross-origin http(s) URLs
  • x-component-styles="title-a,title-b": include matching document stylesheets
  • styles="...": alias for x-component-styles

The directive expression can be static or dynamic. Values are normalized as:

  • string: trimmed and used directly
  • number / boolean / other primitives: converted with String(...)
  • null / undefined / empty string: treated as empty source

When the resolved source is empty, the mounted component is unmounted/cleared.

Render From an On-Page Template

<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>

Render From a URL

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>

Dynamic Template Values

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>

Styles

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>

Slots

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>

Lifecycle Events

The host element emits lifecycle events:

  • x-component:loading when URL loading starts
  • x-component:loaded when render completes
  • x-component:error when 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:error with 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>

Security

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-component or x-component.url
  • Only load templates from your own trusted servers
  • Validate/sanitize any dynamic template selection
  • Use CSP headers for additional protection
  • x-component.url accepts only http(s) URLs
  • x-component.url blocks cross-origin requests by default
  • Use x-component.url.external to opt into cross-origin http(s) requests

Browser Support

This plugin targets modern browsers with support for:

  • Shadow DOM
  • adoptedStyleSheets (when using x-component-styles / styles)
  • CSSStyleSheet (when using x-component-styles / styles)
  • template.content

If your target environment lacks these APIs, use a compatibility strategy or avoid Shadow DOM style adoption features.

Caching

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.

Development

npm install
npm run build

Available scripts:

  • npm run build: lint then build minified CDN + ESM outputs in dist/
  • npm run lint: run ESLint with --fix

Notes

  • Missing templates and failed URL requests are handled with console warnings/errors and lifecycle error events.
  • Expression evaluation failures dispatch x-component:error and 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.

Migration From v1

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.

Stats

About

Reusable HTML components powered by Alpine JS reactivity ๐Ÿ›ธ

Topics

Resources

License

Stars

Watchers

Forks

Contributors