Ir al contenido

Adapters de Alpine, HTMX y Stimulus

Alpine, HTMX y Stimulus comparten una filosofía: el HTML es el componente. El comportamiento se declara con atributos y un runtime recorre el DOM para cablearlo. Por eso los tres adapters tienen la misma forma —un objeto config con un string template— y su mount es esencialmente “inyecta el HTML y luego llama a la función de activación del runtime sobre ese subárbol”.

Alpine HTMX Stimulus
Subpath wu-framework/adapters/alpine wu-framework/adapters/htmx wu-framework/adapters/stimulus
2.º argumento objeto appConfig objeto config objeto config
Config obligatoria template template template, controllers
El runtime se busca en window.Alpine window.htmx import('@hotwired/stimulus') dinámico
Activación Alpine.initTree(container) htmx.process(container) Application.start(container)
Props vivas no no no
Hidratación SSR no no no
Contenedor standalone #app #app #app

Los tres aceptan las mismas opciones: onMount, onUnmount, standalone y standaloneContainer; y los tres lanzan de forma síncrona si falta template o no es un string.


Ventana de terminal
npm install wu-framework

Alpine debe estar en la página como window.Alpine — el build de CDN o window.Alpine = Alpine desde tu bundle.

import { wuAlpine } from 'wu-framework/adapters/alpine';
wuAlpine.register('counter', {
template: `
<div x-data="app">
<span x-text="count"></span>
<button @click="count++">+</button>
</div>
`,
data: () => ({ count: 0 }),
}, {
onMount: (container) => console.log('Montado'),
onUnmount: (container) => console.log('Desmontado'),
});
Clave de appConfig Tipo Notas
template string Obligatorio. HTML con directivas de Alpine.
data function Se registra como Alpine.data('app', fn).

El montaje inyecta el template, llama a Alpine.data('app', data) si pasaste una función data, y luego a Alpine.initTree(container). El desmontaje llama a Alpine.destroyTree(container) cuando está disponible y después vacía el contenedor.

Si falta window.Alpine, el adapter avisa — template inserted but not initialized — y deja el HTML inerte en su sitio en lugar de hacer fallar el montaje.


import { wuHtmx } from 'wu-framework/adapters/htmx';
wuHtmx.register('inbox', {
template: `
<div>
<button hx-get="/messages" hx-target="#list" hx-swap="innerHTML">
Actualizar
</button>
<ul id="list"></ul>
</div>
`,
baseUrl: 'https://api.example.com',
}, {
onMount: (container) => console.log('app HTMX lista'),
});
Clave de config Tipo Notas
template string Obligatorio. HTML con atributos hx-*.
baseUrl string Opcional. Prefija los endpoints HTMX relativos a la raíz.

Tras inyectar el template, el adapter recorre hx-get, hx-post, hx-put, hx-patch y hx-delete y reescribe cualquier valor que empiece por / a baseUrl + valor.

baseUrl: 'https://api.example.com'
// hx-get="/messages" → "https://api.example.com/messages"
// hx-get="messages" → sin cambios (no es relativo a la raíz)
// hx-get="https://x.test/y" → sin cambios (es absoluto)

Esto importa porque el HTML de un micro-frontend se ejecuta en el origen del shell. Un hx-get="/messages" relativo a la raíz iría, si no, al servidor del shell y no al backend de tu micro-app.

La activación es window.htmx.process(container). Sin window.htmx el adapter avisa — template inserted but hx-* attributes will not function — y continúa.

El desmontaje simplemente borra el subárbol, lo que se lleva por delante todos los listeners gestionados por HTMX.


Ventana de terminal
npm install wu-framework @hotwired/stimulus
import { Controller } from '@hotwired/stimulus';
import { wuStimulus } from 'wu-framework/adapters/stimulus';
class SearchController extends Controller {
static targets = ['input', 'results'];
query() {
this.resultsTarget.textContent = `Buscando ${this.inputTarget.value}`;
}
}
wuStimulus.register('search', {
controllers: { search: SearchController },
template: `
<div data-controller="search">
<input data-search-target="input" data-action="input->search#query">
<p data-search-target="results"></p>
</div>
`,
});
Clave de config Tipo Notas
template string Obligatorio. HTML con data-controller / data-action.
controllers object Obligatorio y no vacío. Mapa de identificador → clase Controller.

Ambos se validan de inmediato y lanzan si faltan:

[WuStimulus] template (HTML string) is required for <appName>
[WuStimulus] controllers (object of name -> Controller class) is required for <appName>

A diferencia de un montaje típico de Stimulus con una única Application global, este adapter llama a Application.start(container) — una Application acotada al contenedor. Su MutationObserver solo vigila tu subárbol, así que dos micro-apps de Stimulus en la misma página no pueden verse ni conectarse a los controladores de la otra.

El orden importa en mount: la Application arranca y todos los controladores se registran antes de inyectar el template. El observer de Stimulus los conecta entonces conforme aparecen los nodos data-controller.

@hotwired/stimulus se carga con un import() dinámico, para mantenerlo fuera de la ruta crítica. Si el import falla, el contenedor recibe Failed to load Stimulus runtime y el error se vuelve a lanzar.

El desmontaje llama a application.stop() y vacía el contenedor.


Cada uno de los tres exporta la misma superficie mínima:

import { wuAlpine, register, getWuInstance, waitForWu } from 'wu-framework/adapters/alpine';
import { wuHtmx } from 'wu-framework/adapters/htmx';
import { wuStimulus } from 'wu-framework/adapters/stimulus';

Desde el barrel: registerAlpine, registerHtmx, registerStimulus.

Si necesitas más que un template —estado de componente, estructura basada en clases, un setState programático— mira el adapter de Vanilla, que cubre el mismo territorio sin build con una superficie mucho mayor.