Feature Lab
This page is both an interactive smoke test and a usage cookbook.
Every feature section contains a
live
example followed by an expandable Example usage panel.
1. Inline Templates Auto Discovery
Define components directly in HTML. No JavaScript registration needed.
Example usage
<template acl-component="inline-counter" acl-props='{ "count": "Number", "title": { "type": "String" } }'>
<div>
<h3 x-text="$props.title || 'Default Counter'"></h3>
<button @click="$props.count = ($props.count || 0) + 1">
Clicks <span x-text="$props.count || 0"></span>
</button>
</div>
</template>
<inline-counter persist="session"></inline-counter>
<inline-counter title="Interactive Widget" count="5"></inline-counter>
2. Shadow DOM & Typed Props
Styles are encapsulated. Attributes like active="true" are converted to real Booleans.
Example usage
AlpineComponentLoader.define('shadow-card', 'shadow-card.html', {
shadow: true,
attributes: { title: String, active: Boolean }
});
3. Lifecycle Hooks
Hook into component events: beforeMount, mounted, updated, and
unmounted.
Example usage
AlpineComponentLoader.define('lifecycle-log', 'lifecycle-log.html', {
shadow: true,
attributes: { title: String, logs: Array },
hooks: {
beforeMount() { this.$props.logs.push('beforeMount'); },
mounted() { this.$props.logs.push('mounted'); },
updated({ name }) { this.$props.logs.push(`updated:${name}`); }
}
});
4. External Dependencies
Load external CSS/JS automatically, including FontAwesome icons and the canvas-confetti script.
Loads a demo script before mounting.
Example usage
AlpineComponentLoader.define('external-icon', 'external-icon.html', {
shadow: true,
externalCss: [
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css'
],
attributes: { icon: String, label: String }
});
AlpineComponentLoader.define('confetti-btn', 'confetti.html', {
shadow: true,
externalScripts: ['https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js']
});
5. Strict Validation
Props can enforce required, default values, and custom validator functions.
Example usage
AlpineComponentLoader.define('strict-progress', 'strict-progress.html', {
shadow: true,
attributes: {
percent: {
type: Number,
required: true,
default: 0,
validator: value => value >= 0 && value <= 100
}
}
});
6. Light DOM Slots Polyfill
Use named <slot> elements even without Shadow DOM. CSS is auto-scoped.
Example usage
const lightAlertThemes = {
success: { border: '#bbf7d0', accent: '#16a34a', bg: '#f0fdf4', text: '#14532d' },
error: { border: '#fecaca', accent: '#dc2626', bg: '#fef2f2', text: '#991b1b' },
warning: { border: '#fde68a', accent: '#d97706', bg: '#fffbeb', text: '#92400e' },
info: { border: '#bae6fd', accent: '#0284c7', bg: '#f0f9ff', text: '#0c4a6e' }
};
AlpineComponentLoader.define('light-alert', 'light-alert.html', {
shadow: false,
attributes: { type: String },
hooks: {
mounted({ el, props }) {
const theme = lightAlertThemes[props.type] || lightAlertThemes.info;
el.style.setProperty('--alert-border', theme.border);
el.style.setProperty('--alert-accent', theme.accent);
el.style.setProperty('--alert-bg', theme.bg);
el.style.setProperty('--alert-text', theme.text);
}
}
});
<light-alert type="success">
<span slot="title">Success!</span>
Data saved successfully.
</light-alert>
<light-alert type="error">
<span slot="title">Error!</span>
Data save failed.
</light-alert>
<light-alert type="warning">
<span slot="title">Warning!</span>
Storage is almost full.
</light-alert>
<light-alert type="info">
<span slot="title">Info</span>
Background sync is enabled.
</light-alert>
7. Lazy Loading
This component initializes when it enters the viewport, with a new eager fallback when IntersectionObserver is unavailable.
Example usage
AlpineComponentLoader.define('lazy-image', 'lazy-image.html', {
loading: 'lazy',
loadingTemplate: 'loading-state.html'
});
<lazy-image loading="lazy"></lazy-image>
8. Declarative Loading acl-component
Load components purely via HTML without writing define() in JavaScript.
Example usage
<acl-component src="declarative-card.html" tag="declarative-card" shadow="true" title="Declarative Component" description="Loaded via acl-component." tags='["HTML", "Easy", "NoJS"]' ></acl-component>
9. Declarative Fetching data-src
Provide a data-src attribute to fetch JSON and inject it into $props.$data.
Example usage
<acl-component src="user-card.html" tag="api-user" shadow="true" data-src="/demo-api/user?id=1" ></acl-component>
10. Global Store Binding Two-Way
Components can bind directly to an Alpine global store. Updates sync across all bound instances.
Example usage
document.addEventListener('alpine:init', () => {
Alpine.store('theme', { mode: 'Light', color: '#2563eb' });
});
AlpineComponentLoader.define('store-display', 'store-display.html', {
shadow: true,
bindStore: 'theme'
});
11. Error Boundaries fallback
If a component fails to load, it can render a fallback template instead of exposing a broken UI.
Example usage
AlpineComponentLoader.define('boundary-demo', '#tpl-fallback-error', {
fallback: '#tpl-fallback-error'
});
<boundary-demo></boundary-demo>
12. Idle Loading
Uses requestIdleCallback to load low-priority components when the CPU is idle.
Example usage
<acl-component src="info-card.html" tag="idle-card" loading="idle" shadow="true" title="Idle Loaded" ></acl-component>
13. Dynamic Component Switching
Dynamically swap components using the is attribute. Attributes are forwarded to the active
component.
Example usage
<div x-data="{ currentView: 'shadow-card' }">
<select x-model="currentView">
<option value="shadow-card">Shadow Card</option>
<option value="strict-progress">Strict Progress</option>
<option value="inline-counter">Inline Counter</option>
</select>
<acl-dynamic :is="currentView" keep-alive></acl-dynamic>
</div>
14. Emits Helper $emit
Components can use $props.$emit('name', detail) to dispatch events easily.
Event Output:
Example usage
<template acl-component="emit-demo">
<div>
<button @click="$props.$emit('custom-alert', { message: 'Hello from Shadow DOM!' })">
Emit Event
</button>
</div>
</template>
<div @custom-alert="lastMessage = $event.detail.message">
<emit-demo></emit-demo>
</div>
15. Shared Constructible Stylesheets
Define shared styles once and apply them across Shadow DOM components without duplication.
Example usage
const sharedSheet = new CSSStyleSheet();
sharedSheet.replaceSync(`
.shared-box {
border: 1px solid #99f6e4;
background: #ecfeff;
}
`);
AlpineComponentLoader.config({ sharedStyleSheets: [sharedSheet] });
AlpineComponentLoader.define('shared-style-demo', 'shared-style.html', {
shadow: true
});
16. State Persistence localStorage / sessionStorage / IndexedDB
Each backend uses the same component persistence API. Edit a note, flush it immediately, clear only that component's stored record, or reload the page to see automatic restoration.
Example usage
AlpineComponentLoader.define('persistent-note', 'persistent-note.html', {
shadow: true,
attributes: { note: String, count: Number, storage: String },
persistDebounce: 250
});
<persistent-note persist="local" persist-key="note:local" storage="localStorage"></persistent-note>
<persistent-note persist="session" persist-key="note:session" storage="sessionStorage"></persistent-note>
<persistent-note persist="indexeddb" persist-key="note:indexeddb" storage="IndexedDB"></persistent-note>
const persistence = document.querySelector('[persist="indexeddb"]').$props.$persistence;
const pending = persistence.$save();
await persistence.$flush();
await pending;
await persistence.$clear();
17. Grouped Data API Required
Programmatic data loading uses one data object so request, parsing, retry, polling, and
cache
controls stay together.
Example usage
AlpineComponentLoader.define('grouped-card', 'response-mode.html', {
shadow: true,
attributes: { label: String },
data: {
src: '/api/message',
responseType: 'text',
target: 'payload',
cacheStrategy: 'no-store'
}
});
18. Source Resolver and Base Path New
sourceResolver rewrites aliases before relative paths receive basePath.
Absolute and root-relative paths stay untouched.
Example usage
AlpineComponentLoader.config({
basePath: './components/',
sourceResolver(source) {
return source === 'demo:resolver' ? 'source-result.html' : source;
}
});
19. Advanced Data Fetching New
Data requests now support method, body, headers, retries, custom target props, and request-aware cache keys.
Example usage
<acl-component
src="advanced-fetch.html"
tag="declarative-fetch-demo"
data-method="POST"
data-body='{ "declarative": true }'
data-target="audit"
data-response-type="json"
data-retries="1"
data-retry-max-delay="1000"
data-retry-jitter="0"
data-retry-unsafe-methods="true"
data-cache-strategy="no-store"
data-fetch-cache-ttl="0"
data-fetch-cache-max="0"
data-cache-key="feature-lab-declarative"
></acl-component>
20. Template and Data Cache Controls New
Components can clear their own data cache, reload on demand, and opt into revision-aware template cache entries with freshness and capacity bounds. Clearing this example also resets its demo request counter, so the next reload visibly starts at one.
Inspect the persistent template cache to see revisions, TTLs, and access times.
Example usage
<cache-control-demo></cache-control-demo> // Clear the current component's exact request-aware cache entry. await element.$props.$cache.clearData(); await element.$props.$reload(); const info = await AlpineComponentLoader.getTemplateCacheInfo(); const evicted = await AlpineComponentLoader.pruneTemplateCache(); <acl-component src="info-card.html" tag="template-network-demo" template-cache-strategy="network-first" ></acl-component>
21. Loading Templates and Safe Mode New
Loading UI renders before data work completes, and secure rendering strips scripts plus inline handlers from rendered content.
Example usage
AlpineComponentLoader.define('secure-render-demo', '#tpl-unsafe-content', {
shadow: true,
executeScripts: false, sanitize: true
});
22. Mapped Event Forwarding New
Forwarded events can be renamed, and reloads clean up old listeners so handlers do not fire twice.
Example usage
AlpineComponentLoader.define('mapped-event-demo', 'mapped-event.html', {
shadow: true,
events: {
forward: [{ from: 'internal-save', as: 'public-save' }]
}
});
23. Floating Debugger Panel New
The debug button opens the inspector with component search and status filters, selected props, request and cache activity, accessibility results, state snapshots and diffs, scroll-to-element, reload, and clear-cache actions.
The scanner audits every active ACL component; the debugger receives the same
composed acl:a11y results.
Run the focused audit or open the scanner to inspect the intentional findings.
Example usage
import ACLA11y from 'alpine-component-loader/a11y';
import ACLA11yScanner from 'alpine-component-loader/a11y-scanner';
ACLDebugger.inject(AlpineComponentLoader);
const audits = ACLA11y.observe(AlpineComponentLoader);
await audits.audit(document.querySelector('a11y-issues-demo'));
ACLA11yScanner.mount().open();
AlpineComponentLoader.toggleDebug();
24. Registry, Manifests, Prefetch & Template Observation 1.0
Inspect immutable definitions, validate component contract metadata, register version-one dependency graphs, prefetch transitive groups with bounded concurrency, and discover templates inserted after startup.
Observer is ready to start.
Run “Inspect registry” to see snapshots and settled prefetch results.
Example usage
const result = await AlpineComponentLoader.registerManifest({
version: 1,
components: {
'manifest-base': 'info-card.html',
'manifest-card': {
source: 'info-card.html',
dependencies: ['manifest-base'],
options: { shadow: true, attributes: { title: String } },
metadata: {
description: 'A manifest-backed card.',
events: {
'manifest-ready': {
detail: {
type: 'object',
properties: {
id: { type: 'string' },
ready: { type: 'boolean' }
},
required: ['id', 'ready']
}
}
},
slots: { default: { description: 'Additional content.' } }
}
},
'adaptive-card': {
source: 'source-result.html',
dependencies: ['manifest-base'],
options: { shadow: true }
}
},
groups: {
dashboard: ['manifest-card'],
preview: ['adaptive-card']
}
}, { prefetch: ['dashboard'], concurrency: 2 });
const stop = AlpineComponentLoader.observeTemplates();
AlpineComponentLoader.has('manifest-card');
AlpineComponentLoader.getDefinition('manifest-card');
AlpineComponentLoader.getDependencies('manifest-card', { transitive: true });
await AlpineComponentLoader.prefetchGraph(['manifest-card'], { concurrency: 4 });
stop();
25. Advanced Prop Contracts Coerce · Schema · Reflect
Prop definitions support factories, nullable values, enums, custom coercion and validation, nested schemas, property accessors, and optional attribute reflection.
Example usage
AlpineComponentLoader.define('advanced-props-demo', 'advanced-props.html', {
shadow: true,
strictProps: true,
attributes: {
mode: {
type: String,
options: ['compact', 'comfortable'],
default: 'comfortable',
reflect: true
},
score: {
type: Number,
coerce: value => Number(value.replace('%', '')),
validator: value => value >= 0 && value <= 100,
reflect: true
},
caption: { type: String, nullable: true },
tags: { type: Array, default: () => [] },
profile: { type: Object, schema: { name: String, active: Boolean } }
}
});
const card = document.querySelector('advanced-props-demo');
card.score = 88; // Updates $props and reflects score="88"
26. Response Types & Custom Parsers 7 modes
The fetch layer can decode JSON, text, vendor JSON automatically, blobs, array buffers, streams, or a user-supplied parser. Binary and streaming responses bypass shared caching unless explicitly keyed.
Example usage
AlpineComponentLoader.define('binary-report', 'response-mode.html', {
data: {
src: '/report.bin',
target: 'payload',
responseType: 'arrayBuffer',
cacheStrategy: 'no-store'
},
hooks: {
afterFetch(buffer) {
return new TextDecoder().decode(buffer);
}
}
});
AlpineComponentLoader.define('custom-report', 'response-mode.html', {
data: {
src: '/report.pipe',
parser: async response => (await response.text()).split('|')
}
});
27. Polling, Cancellation & Recovery Resilient fetch
Polling can pause while hidden, offline, or outside the viewport. Requests can be canceled and retried
through component methods or the built-in $cancel, $retry, and
$reload helpers.
Example usage
AlpineComponentLoader.define('live-feed', 'polling.html', {
data: {
src: '/api/feed',
poll: 1000,
timeout: 5000,
retries: 3,
retryDelay: 250,
retryMaxDelay: 10000,
retryJitter: 0.2,
cacheStrategy: 'no-store',
pauseWhenHidden: true,
pauseWhenOffline: true,
pauseWhenOffscreen: true
}
});
// Unsafe methods require an explicit retry opt-in.
data: { method: 'POST', retries: 2, retryUnsafeMethods: true }
$props.$cancel('User canceled');
await $props.$retry();
28. Runtime Events & Typed Errors Observable
Namespaced events expose load, cache, revalidation, and failure activity. Errors carry stable
code, phase, status, and retryable fields.
Runtime events will appear here.
Example usage
document.addEventListener('acl:error', event => {
const { error, phase } = event.detail;
console.log(error.code, phase, error.status, error.retryable);
});
document.addEventListener('acl:cachehit', event => {
console.log(event.detail.url, event.detail.strategy);
});
import { ACLLoadError } from 'alpine-component-loader';
throw new ACLLoadError('Unavailable', {
code: 'APP_UNAVAILABLE', phase: 'fetch', status: 503, retryable: true
});
29. Async Lifecycle, Cleanup & Keep-Alive Deterministic
Hooks can await work and return cleanup callbacks. Kept-alive components receive activation and deactivation hooks while polling and observers pause safely.
Example usage
hooks: {
async beforeMount({ props }) {
await prepare();
props.ready = true;
},
mounted({ el }) {
const controller = new AbortController();
el.addEventListener('refresh', refresh, { signal: controller.signal });
return () => controller.abort();
},
activated({ props }) { props.active = true; },
deactivated({ props }) { props.active = false; },
loaded() { /* Alpine initTree is complete */ }
}
30. Dynamic Transitions, Focus & Bounded Keep-Alive Accessible
The dynamic loader supports native view, fade, scale, slide, blur, and no-motion transitions, stale-switch cancellation, focus restoration, reduced-motion preferences, attribute forwarding, and an LRU-style keep-alive bound.
Example usage
<acl-dynamic
is="profile-card"
transition="auto"
transition-duration="140"
keep-alive
keep-alive-max="2"
title="Forwarded prop"
></acl-dynamic>
AlpineComponentLoader.config({
dynamicTransition: 'auto',
transitionDuration: 140,
keepAliveMax: 2
});
31. Persistence Adapters & Schema Migration localStorage / sessionStorage / IndexedDB
Custom adapters support the same storage backends and component persistence API as state persistence. Each backend starts with a version 1 record, migrates it to version 2, and supports automatic saves, explicit flushes, scoped clears, and safe storage error boundaries.
The seeded version 1 records are migrated to version 2 during mount.
Example usage
import { createIndexedDBPersistenceAdapter } from 'alpine-component-loader';
const adapters = {
local: window.localStorage,
session: window.sessionStorage,
indexeddb: createIndexedDBPersistenceAdapter({
databaseName: 'settings',
storeName: 'component-state'
})
};
AlpineComponentLoader.define('settings-panel', '#settings', {
persist: 'custom',
persistAdapter: adapters.indexeddb,
persistKey: 'settings:user-42',
persistVersion: 2,
persistDebounce: 100,
async persistMigrate(data, { fromVersion }) {
return fromVersion < 2 ? { ...data, theme: 'system' } : data;
}
});
await $props.$persistence.$save();
await $props.$persistence.$flush();
await $props.$persistence.$clear();
32. Sanitization, CSP & Asset Descriptors Secure by default
Declarative values are strict JSON, while scripts are disabled and rendered fragments are sanitized by default. Custom sanitizers and CSP-aware asset descriptors add application-specific policy.
Example usage
AlpineComponentLoader.define('secure-card', '#secure-card', {
executeScripts: false,
sanitize(fragment) {
fragment.querySelectorAll('[data-private]').forEach(node => node.remove());
},
externalCss: [{
url: '/assets/card.css',
integrity: 'sha384-…',
crossOrigin: 'anonymous',
referrerPolicy: 'no-referrer',
media: 'screen',
timeout: 5000
}],
externalScripts: [{ url: '/assets/vendor.js', nonce: window.cspNonce }]
});
33. Diagnostics, Cache Introspection & Export Redacted
Inspect template/data caches and create a redacted, versioned diagnostic snapshot containing component state and the bounded lifecycle timeline.
Create a snapshot to inspect the live page.
Example usage
import ACLDebugger, {
createComponentSnapshot,
createDiagnosticSnapshot,
diffDiagnosticSnapshots,
redactDiagnostics
} from 'alpine-component-loader/debugger';
ACLDebugger.inject(AlpineComponentLoader);
const snapshot = ACLDebugger.getSnapshot(AlpineComponentLoader);
AlpineComponentLoader.getDataCacheSize();
AlpineComponentLoader.getDataCacheInfo('/api/user');
AlpineComponentLoader.getTemplateLoadInfo('/components/card.html');
await AlpineComponentLoader.getTemplateCacheInfo();
await AlpineComponentLoader.pruneTemplateCache();
await AlpineComponentLoader.clearTemplateCaches();
AlpineComponentLoader.clearDataCache();
34. Entry Points, SSR, HMR & TypeScript Production integration
The root package is side-effect free and SSR-safe. Browser auto-start, debugger, accessibility, testing helpers, offline registration, and development reloads live in dedicated tree-shakeable entries, all backed by generated declarations.
alpine-component-loader is powering this page.alpine-component-loader/debugger is injected and ready.
alpine-component-loader/a11y feeds optional audits to the
debugger.
alpine-component-loader/offline explicitly registers generated
workers.
alpine-component-loader/testing mounts components, records
events,
and mocks fetch in browsers.Continue to the focused SSR feature lab, HMR example, and offline example for workflows that require server or service-worker behavior.
Example usage
// Explicit, side-effect-free entry (recommended)
import AlpineComponentLoader from 'alpine-component-loader';
AlpineComponentLoader.define('site-card', '/components/card.html');
await AlpineComponentLoader.start();
// Browser-only convenience entry
import AlpineComponentLoader from 'alpine-component-loader/auto';
// Development-only selective template reloads
import { connectACLDevServer } from 'alpine-component-loader/dev';
const hmr = connectACLDevServer({ url: 'http://localhost:3000/__acl_hmr/events' });
hmr.close();
// Optional development accessibility auditing
import ACLA11y from 'alpine-component-loader/a11y';
const audits = ACLA11y.observe(AlpineComponentLoader);
// Browser test helpers
import { mountComponent, recordACLEvents } from 'alpine-component-loader/testing';
// Explicit generated service-worker registration
import { registerOfflineWorker } from 'alpine-component-loader/offline';
await registerOfflineWorker('/offline/acl-sw.js');
// Generate TypeScript and Custom Elements contracts from an enriched manifest.
// npx alpine-component-loader types acl-manifest.json \
// --out generated/acl-components.d.ts \
// --custom-elements-out generated/custom-elements.json
// Node/SSR imports are safe; start() intentionally requires DOM APIs.
const { default: Loader } = await import('alpine-component-loader');
35. Adaptive Prefetch Hover · Focus · Direct
Warm a manifest group and its dependencies before navigation. The observer responds to intentional hover or focus, while the controller also supports deterministic direct requests and complete cleanup.
Interact with a prefetch target or run a direct request.
Example usage
const controller = await AlpineComponentLoader.observePrefetch({
triggers: ['hover', 'focus'],
hoverDelay: 75,
concurrency: 2
});
// <button data-acl-prefetch="preview">Preview</button>
const results = await controller.prefetch('preview');
addEventListener('acl:prefetchstart', event => console.log(event.detail.tags));
addEventListener('acl:prefetchend', event => console.log(event.detail.fulfilled));
addEventListener('acl:prefetchskip', event => console.log(event.detail.reason));
controller.disconnect();
AlpineComponentLoader.stopObservingPrefetch();
36. Structured Observability & Performance Metrics Local · Bounded · Redacted
Subscribe to live redacted records, retain bounded counters and durations, and correlate component loads with browser Performance API measures without sending telemetry anywhere.
Run the measurement to inspect totals, durations, and performance measures.
Live subscription records appear here.
Example usage
AlpineComponentLoader.config({
observability: {
bufferSize: 80,
performanceMarks: true,
logger: false
}
});
const unsubscribe = AlpineComponentLoader.subscribe(record => {
console.log(record.type, record.phase, record.detail);
});
const snapshot = AlpineComponentLoader.getMetrics();
AlpineComponentLoader.clearMetrics();
unsubscribe();
37. Trusted Types & URL Policies Application security boundary
A fetched template passes through an application-owned Trusted Types policy and a same-origin URL allowlist. Approved links survive sanitization while the external tracking link loses its URL.
Inspect the rendered component to see policy calls and sanitized URLs.
Example usage
const createHTML = html => html;
const policy = window.trustedTypes?.createPolicy('acl-app', { createHTML })
|| { createHTML };
AlpineComponentLoader.define('secure-links', 'secure-links.html', {
sanitize: true,
security: {
trustedTypesPolicy: policy,
urlPolicy(value) {
return new URL(value, document.baseURI).origin === location.origin;
}
}
});
38. Browser Testing Utilities Mount · Mock · Record · Unmount
Run the public testing entry in this page: mock one request, mount an isolated inline component, wait for readiness, update and reload it, record lifecycle events, then destroy it and restore fetch.
Run the helper demo to inspect its request and lifecycle results.
Example usage
import {
installFetchMock,
mountComponent,
recordACLEvents,
waitForComponent
} from 'alpine-component-loader/testing';
const recorder = recordACLEvents(fixture);
const mock = installFetchMock([
{ match: /\/api\/profile$/, response: { name: 'Ada' } }
]);
const mounted = await mountComponent({
container: fixture,
template: '<strong x-text="$props.name"></strong>',
attributes: { name: 'Ada' }
});
await waitForComponent(mounted.element);
await mounted.update({ attributes: { name: 'Grace' } });
await mounted.unmount();
recorder.stop();
mock.restore();