Cómo integrar SortableJS con Alpine para crear listas ordenables (drag & drop)

Video thumbnail

Cuando trabajas con Alpine y quieres añadir interacción avanzada sin instalar librerías gigantes, SortableJS es de esos salvavidas que te resuelven la vida. Lo usé por primera vez en una app tipo to-do list en Alpine JS, y desde entonces se volvió parte de mi “caja de herramientas” para todo lo que implica reordenar elementos con drag & drop.

A continuación te muestro cómo integrarlo paso a paso usando CDN, al igual que hicimos antes con la persistencia de datos en Alpine. Cómo inicializarlo dentro de Alpine y cómo aprovecharlo en un caso práctico real.

Qué es SortableJS y por qué combinarlo con Alpine

SortableJS es una librería de JavaScript puro que permite convertir cualquier lista en una lista ordenable arrastrando elementos.

Lo interesante es que no es un plugin específico de Alpine —como te comentaba la primera vez que lo probé—, pero eso no es un problema: Alpine tiene hooks suficientes para integrarlo perfectamente.

Ventajas de usarlo junto con Alpine:

  • No necesitas dependencias adicionales.
  • Puedes controlar el estado desde Alpine sin reescribir nada.
  • El comportamiento es muy estable incluso en listas largas.
  • Puedes disparar eventos después de mover un ítem para sincronizar datos.

La siguiente operación que vamos a realizar es permitir que el estado sea ordenable, es decir, ordenable mediante drag and drop. Que podamos seleccionar un ítem y ubicarlo en otra parte.

Para esto, es muy fácil. Ya existe un plugin en JavaScript, es decir, no forma parte de Alpine, pero lo podemos integrar fácilmente. El plugin se llama SortableJS. Escribimos “sortable js” (de ordenable) y entramos al primer enlace. Como te comentaba, no es un plugin específico para Alpine, sino que es puro JavaScript.

Aquí puedes ver algunos demos de cómo funciona: damos clic, lo sostenemos y lo arrastramos. Y, como te digo, existen muchas implementaciones posibles: para compartir, ordenar, etc.

Instalación con CDN

A nosotros nos interesa instalarlo mediante la CDN. Puedes buscar “sortable js cdn”. Si no encuentras nada al principio, puedes ir directamente a la página de jsDelivr o similar. En este caso, encontramos la CDN, la copiamos y la pegamos en nuestro proyecto.

Yo la voy a colocar justo después del script de Alpine, para mantener el orden. Su uso es muy sencillo, como puedes ver en la documentación oficial. Básicamente, consiste en obtener el listado de elementos mediante un selector y luego aplicar Sortable.create, pasándole el elemento. Con eso, el contenido ya sería ordenable. Bastante simple.

<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js" defer></script>

Integración con Alpine

Ahora bien, como este plugin no forma parte del ecosistema de Alpine, tenemos que ingeniárnoslas un poco para integrarlo. En Alpine tenemos un atributo llamado x-init, que se ejecuta cuando se crea el componente. Es decir, actúa como una especie de constructor, y lo podemos aprovechar para hacer esta inicialización.

Entonces, lo que haremos será referenciar nuestro listado —que es lo que queremos hacer ordenable— mediante x-ref, aunque perfectamente puedes usar un selector como los que se muestran en la documentación oficial (por ejemplo, document.querySelector, getElementById, etc.).

En uno de los ejemplos, puedes ver que se referencia así directamente. Pero como a nosotros nos gusta complicarnos la vida, vamos a usar x-ref.

Una vez instalado el plugin, verificamos que no tengamos errores 404. Todo perfecto. Entonces ya podemos comenzar.

Voy a colocar un x-init="order" para que todo quede organizado. Este método order lo vamos a crear justo debajo de data. Lo colocamos así:

order() {
  console.log("Inicializando sortable...");
}

Aquí puedes ver que, cuando se crea el componente, se llama este método. Muy sencillo. En este método aprovechamos para hacer la inicialización de Sortable.

Sortable.create(this.$refs.items);

Aquí le pasamos el ref que definimos como items. Entonces, en nuestro HTML colocamos:

<div x-data="{
        items: ['Uno', 'Dos', 'Tres'],
        order() {
            Sortable.create(this.$refs.items, {
                animation: 150
            });
        }
    }"
    x-init="order"
>
    <ul x-ref="items">
        <template x-for="item in items" :key="item">
            <li class="px-3 py-2 bg-gray-100 mb-1 cursor-move" x-text="item"></li>
        </template>
    </ul>
</div>

Con esto ya debería funcionar todo correctamente.

Opciones útiles

  • animation: suaviza el movimiento.
  • handle: área específica donde se puede arrastrar.
  • ghostClass: clase que se aplica al elemento “fantasma” mientras arrastras.

Caso práctico: lista tipo to-do reordenable

Te comparto una versión simplificada de la app tipo to-do list que usé en un curso.
La gracia está en dos cosas:

  • Integrar SortableJS dentro de Alpine.
  • Sincronizar el estado después de que el usuario mueve los ítems.
<div x-data="todoList()" x-init="init">
   <h2 class="font-bold text-xl mb-3">Mi To-Do List Ordenable</h2>
   <ul x-ref="tasks" class="space-y-2">
       <template x-for="(task, index) in tasks" :key="task.id">
           <li class="p-3 bg-blue-100 rounded cursor-move" x-text="task.label"></li>
       </template>
   </ul>
</div>
<script>
function todoList() {
   return {
       tasks: [
           { id: 1, label: 'Revisar documentación' },
           { id: 2, label: 'Crear ejemplo con Alpine' },
           { id: 3, label: 'Probar integración con SortableJS' },
       ],
       init() {
           Sortable.create(this.$refs.tasks, {
               animation: 150,
               onEnd: (evt) => {
                   // Reordenar array según nueva posición
                   const moved = this.tasks.splice(evt.oldIndex, 1)[0];
                   this.tasks.splice(evt.newIndex, 0, moved);
               }
           });
       }
   }
}
</script>

Esto garantiza que la interfaz y el estado interno quedan sincronizados.

Es la parte más “mágica” del proceso, y una de las razones por las que me encanta combinar Alpine con librerías externas.

Resumen

Para resumir un poco:

  • Utilizamos x-init para inicializar nuestro componente, ideal para este tipo de plugins externos como SortableJS.
  • Empleamos x-ref como alternativa a querySelector, getElementById, etc.
  • Vimos cómo instalar y usar un plugin que no forma parte del ecosistema de Alpine directamente en nuestros componentes.
  • Con esto puedes enriquecer tu interfaz sin necesidad de herramientas más pesadas.

Consejos, problemas comunes y buenas prácticas

  • Si SortableJS no funciona, revisa que el componente Alpine donde lo llamas exista en el DOM.
  • Evita usar x-for con claves inexistentes (usa siempre :key="id").
  • Si vas a manejar miles de elementos, reduce la animación o deshabilítala para ahorrar recursos.
  • Para proyectos grandes, crea funciones Alpine separadas para inicializar las listas.
  • Si tienes varias listas conectadas, usa la opción group.

Conclusión

Integrar SortableJS con Alpine es sorprendentemente sencillo.
Solo necesitas un x-init, un x-ref y un par de líneas de inicialización.
En mi caso lo integré primero en un proyecto pequeño de to-do y me funcionó tan fluido que lo adopté para paneles, dashboards y hasta formularios dinámicos.

Si quieres crear experiencias más limpias sin depender de frameworks pesados, esta combinación es de las mejores que existen.

Preguntas frecuentes

  • ¿Puedo usar SortableJS sin Alpine?
    • Sí, SortableJS funciona solo, pero Alpine permite controlar el estado más fácilmente.
  • ¿Qué pasa si uso varios componentes en la misma página?
    • Nada especial, pero debes asegurarte de que cada lista tenga su propio x-ref.
  • ¿Dónde es mejor inicializar SortableJS dentro de Alpine?
    • En x-init, porque actúa como constructor del componente.
  • ¿Cómo actualizo el estado después de mover un elemento?
    • Captura el evento onEnd de SortableJS y reordena el array interno (como en el ejemplo del to-do list)

Acepto recibir anuncios de interes sobre este Blog.

Aprende a integrar SortableJS con Alpine.js para crear listas reordenables con drag and drop. Este tutorial te guía paso a paso para añadir interactividad a tus proyectos sin librerías pesadas, sincronizando el estado fácilmente.

| 👤 Andrés Cruz

🇺🇸 In english