Alpine.js suele ser suficiente
La mayoría de los sitios de contenido y marketing necesitan tres cosas de JavaScript: algo que se abra, algo que se cierre y algo que recuerde una preferencia. Eso no da para un framework, y aun así es muy fácil recurrir a uno.
Alpine.js es nuestra opción por defecto en esta categoría, y la razón no es realmente el tamaño del archivo. Es que Alpine no opina sobre cómo debe estructurarse tu aplicación: adjunta comportamiento al marcado que ya tienes.
El comportamiento vive en el elemento
Todo el modelo son unos pocos atributos sobre el elemento al que afectan:
<div x-data="{ open: false }">
<button x-on:click="open = ! open">Menú</button>
<nav x-show="open" x-cloak>
<a href="/articulos">Artículos</a>
</nav>
</div>
No hay componente que registrar, ni paso de compilación, ni raíz que montar. Quien lea ese marcado dentro de un año verá exactamente qué hace sin abrir otro archivo, que es el verdadero argumento a favor de Alpine en un sitio de contenido, donde quien mantiene una plantilla rara vez es quien la escribió.
Un ejemplo real
El interruptor de modo oscuro de este sitio es Alpine, y esta es la implementación completa:
<button type="button"
x-data="{
dark: document.documentElement.classList.contains('dark'),
toggle() {
this.dark = ! this.dark;
document.documentElement.classList.toggle('dark', this.dark);
localStorage.setItem('theme', this.dark ? 'dark' : 'light');
}
}"
x-on:click="toggle()">
<svg x-show="! dark">...</svg>
<svg x-show="dark" x-cloak>...</svg>
</button>
Un detalle fácil de equivocar: la clase debe aplicarse antes del primer pintado, o la página parpadea con el tema equivocado mientras Alpine arranca. Eso corresponde a un pequeño script en línea dentro del head, no a Alpine:
<script>
(() => {
const stored = localStorage.getItem('theme');
const dark = stored ? stored === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches;
document.documentElement.classList.toggle('dark', dark);
})();
</script>
Alpine se limita después a leer la clase que ese script ya dejó puesta. Saber dónde no debe usarse Alpine es buena parte de usarlo bien.
Impórtalo de verdad
Alpine se adopta a menudo como dependencia transitiva: Livewire lo incluye, así que aparece en la página sin haberlo importado nunca. Eso funciona hasta que eliminas Livewire, momento en el que cada x-data del sitio deja de funcionar en silencio.
Nos pasó exactamente eso aquí. Impórtalo de forma explícita y la dependencia queda honesta:
import Alpine from 'alpinejs';
window.Alpine = Alpine;
Alpine.start();
x-cloak no es opcional
Entre la carga de la página y la inicialización de Alpine, los elementos controlados por x-show son visibles. x-cloak los oculta hasta que Alpine toma el control, y solo funciona si le das estilo:
[x-cloak] {
display: none !important;
}
Si falta esa regla, todos los desplegables del sitio parpadean abiertos al cargar. Es el fallo de Alpine que más vemos, y es un problema de CSS, no de JavaScript.
Dónde deja de ser la respuesta correcta
Alpine no sustituye a un framework de componentes, y fingir lo contrario produce código malo. En cuanto tengas estado compartido entre partes distantes de la página, enrutado real en el cliente, o un modelo de datos que deba mantenerse consistente en varios sitios a la vez, querrás algo con un árbol de componentes de verdad.
La señal es la longitud de las expresiones. Cuando x-data deja de caber cómodamente en pantalla, o te descubres despachando eventos personalizados para mantener sincronizadas dos islas de marcado, Alpine ha dejado de ser la opción sencilla.
Hasta entonces es difícil de superar: sin paso de compilación, sin runtime de framework que enviar, y con el comportamiento allí donde quien mantenga el código va a buscarlo.