Tag: Angular

  • Angular 19 con Signals y Effects: Reactividad moderna sin RxJS

    Angular 19 con Signals y Effects: Reactividad moderna sin RxJS

    Con la llegada de Angular 16, y su consolidación en Angular 17, 18 y ahora 19, el equipo de Angular ha introducido una nueva forma de trabajar con la reactividad que se aleja de la complejidad de RxJS en muchos casos comunes: los Signals y Effects.

    En este post, exploraremos cómo crear un proyecto moderno con Angular 19 usando esta nueva API reactiva, y compararemos la solución con un enfoque clásico basado en BehaviorSubject. También presentaremos herramientas como Volta para controlar versiones de Node.js de forma consistente.

    Requisitos previos

    Versiones que usaremos:

    • Angular: 19.0.0 (estable en junio 2025)
    • Node.js: 20.x LTS
    • NPM: 10.x
    • Volta: última estable

    ¿Qué es Volta y por qué usarlo?

    Volta es un gestor de herramientas para entornos JavaScript que permite instalar y fijar versiones de herramientas como Node.js, npm, Yarn o pnpm. A diferencia de NVM, es más rápido, automático y confiable, ideal para entornos de desarrollo modernos y consistentes.

    Instalación de Volta

    En macOS con Homebrew:

    brew install volta

    O con el script oficial:

    curl https://get.volta.sh | bash

    Configuración del entorno

    Una vez instalado:

    volta install node@20
    volta install npm
    volta pin node@20

    Esto asegura que todos los comandos dentro del proyecto usen siempre Node.js 20.x, sin necesidad de .nvmrc o pasos manuales.

    Volta detecta automáticamente la configuración guardada en el package.json y la aplica en CLI, IDEs o entornos de CI/CD.

    Crear proyecto Angular

    Durante la creación del proyecto, Angular puede preguntarte si deseas habilitar SSR (Server-Side Rendering) o SSG (Static Site Generation). Para este tutorial, responde “No” a ambas, ya que estamos construyendo una SPA simple que consume una API pública desde el navegador.

    Instalación del Angular CLI

    Si aún no tienes Angular CLI instalado globalmente, puedes hacerlo con:

    npm install -g @angular/cli@19

    Esto te permitirá usar el comando ng para crear y administrar proyectos Angular.

    npm create @angular@latest
    # o si ya tienes Angular CLI
    ng new pokemon-signals-demo --standalone --routing --style=css
    cd pokemon-signals-demo

    Agregar Tailwind CSS al proyecto

    Desde Tailwind CSS v4, la forma recomendada de integración con Angular ha cambiado. Ya no se utiliza @tailwind en archivos SCSS ni se genera postcss.config.js, sino que se usa un archivo .postcssrc.json.

    Paso 1: Instalar Tailwind CSS

    npm install tailwindcss @tailwindcss/postcss postcss --force

    Paso 2: Crear el archivo .postcssrc.json

    En la raíz del proyecto, crea un archivo llamado .postcssrc.json con el siguiente contenido:

    {
      "plugins": {
        "@tailwindcss/postcss": {}
      }
    }

    Paso 3: Crear y configurar el archivo de estilos global

    Asegúrate de usar un archivo styles.css (no SCSS) dentro de src/, y en él escribe:

    Luego actualiza el archivo angular.json para asegurarte de que src/styles.css sea la hoja de estilo principal.

    Importante: Tailwind v4 no es compatible con preprocesadores CSS como SCSS. Usa solo .css para los estilos globales.

    Paso 4: Ejecutar la aplicación

    ng serve

    Qué son Signals y Effects

    signal es una forma de declarar un valor reactivo sin depender de RxJS. Funciona como una señal reactiva que notifica automáticamente a Angular para actualizar la vista o ejecutar efectos cuando cambia su valor.

    • signal: crea un valor observable reactivo.
    • computed: deriva valores de otros signals.
    • effect: ejecuta efectos secundarios (como peticiones HTTP o logs) en respuesta a cambios.

    Ejemplo real: Buscador de Pokémon con Angular Signals

    Vamos a crear un componente que consulte la API pública de PokeAPI y filtre los primeros 151 Pokémon por nombre.

    Crear el componente, servicio, tipos y configurar HttpClient

    Primero, generamos un componente standalone:

    ng generate component PokemonSearch --standalone

    También generamos un servicio:

    ng generate service pokemon --flat --path=src/app/pokemon

    Y creamos una carpeta para los tipos e interfaces del dominio:

    mkdir src/app/pokemon/types

    Dentro creamos el archivo de modelo:

    // src/app/pokemon/types/pokemon.model.ts
    export interface Pokemon {
      name: string;
      url: string;
    }

    Este servicio manejará la comunicación con la API. La interfaz Pokemon se separa en un archivo tipo para mantener una estructura limpia:

    import { Injectable, signal } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { Pokemon } from '../types/pokemon.model';
    
    @Injectable({ providedIn: 'root' })
    export class PokemonService {
      private readonly apiUrl = 'https://pokeapi.co/api/v2/pokemon?limit=151';
    
      readonly pokemons = signal<Pokemon[]>([]);
    
      constructor(private http: HttpClient) {
        this.fetchAll();
      }
    
      fetchAll() {
        this.http.get<any>(this.apiUrl).subscribe(response => {
          this.pokemons.set(response.results);
        });
      }
    }
    

    Configurar main.ts para standalone con HttpClient

    Edita main.ts para usar PokemonSearchComponent como raíz:

    import { bootstrapApplication } from '@angular/platform-browser';
    import { provideHttpClient } from '@angular/common/http';
    import { PokemonSearchComponent } from './app/pokemon-search/pokemon-search.component';
    
    bootstrapApplication(PokemonSearchComponent, {
      providers: [
        provideHttpClient(),
      ]
    });

    Ver el resultado en el navegador

    Dado que PokemonSearchComponent es un componente standalone y está siendo bootstrapped directamente en main.ts, debes asegurarte de que su selector esté presente en index.html.

    Por defecto, el selector del componente es:

    selector: 'app-pokemon-search'

    Entonces, en tu archivo src/index.html, dentro del <body>, agrega:

    <body>
      <app-pokemon-search></app-pokemon-search>
    </body>

    Esto le dice a Angular dónde montar el componente en el DOM. Si no colocas ese selector, obtendrás un error como:

    NG05104: The selector "app-pokemon-search" did not match any elements

    También podrías cambiar el selector a 'app-root' si prefieres mantener la convención típica de Angular y usar <app-root> en el HTML.

    Dado que PokemonSearchComponent es un componente standalone y está siendo bootstrapped directamente en main.ts, el contenido se mostrará en el index.html por defecto de Angular, en el elemento <app-root> (o en su reemplazo si se cambia el selector).

    Asegúrate de que el selector usado sea <app-pokemon-search> o lo que corresponda, y que esté vinculado directamente desde main.ts como se muestra arriba.

    Vamos a construir una aplicación que cargue la lista de los primeros 151 Pokémon desde la API pública PokeAPI y permita filtrarlos por nombre con un input reactivo.

    import { Component, computed, signal, effect } from '@angular/core';
    import { CommonModule } from '@angular/common';
    import { PokemonService } from '../pokemon/pokemon.service';
    
    @Component({
      selector: 'app-pokemon-search',
      standalone: true,
      imports: [CommonModule],
      templateUrl: './pokemon-search.component.html',
    })
    export class PokemonSearchComponent {
      // Signal que almacena el valor actual del input
      searchTerm = signal('');
    
      // Valor computado que depende del signal searchTerm y del signal del servicio
      filteredPokemons = computed(() => {
        const term = this.searchTerm().toLowerCase();
        return this.pokemonService.pokemons().filter(p =>
          p.name.toLowerCase().includes(term)
        );
      });
    
      // Inyectamos el servicio donde vive el signal con la data
      constructor(public pokemonService: PokemonService) {
        // Opcional: efecto secundario al cambiar el valor del input
        effect(() => {
          console.log('Término de búsqueda:', this.searchTerm());
        });
      }
    
      // Función que actualiza el signal searchTerm al escribir en el input
      updateSearch(term: string) {
        this.searchTerm.set(term);
      }
    }
    

    ¿Qué son signal, computed y effect?

    • signal(initialValue): Crea un valor reactivo que notifica automáticamente cuando cambia. Similar a un BehaviorSubject, pero más simple y sin necesidad de subscribe.

    • computed(() => ...): Deriva un valor a partir de uno o varios signals. Solo se recalcula cuando cambian sus dependencias. Ideal para filtros, cálculos derivados, etc.

    • effect(() => ...): Ejecuta un bloque de código cada vez que cambian los signals usados dentro del efecto. Útil para side-effects como logging, tracking, llamadas a APIs, etc.

    En este tutorial solo usamos effect como demostración, pero podrías aprovecharlo para:

    • Guardar búsquedas recientes.

    • Sincronizar con el localStorage.

    • Disparar una petición HTTP cada vez que cambie el filtro (aunque no es necesario en este caso porque ya precargamos los 151 Pokémon).

    Template HTML

    <div class="flex flex-col items-center justify-center h-screen w-screen">
      <div class="w-4/5 min-h-[500px] bg-gray-100 rounded-lg p-4 overflow-y-auto my-8">
        <input
          type="text"
          class="w-full p-2 rounded-md border border-gray-300 bg-white"
          placeholder="Buscar Pokémon..."
          (input)="updateSearch($any($event).target.value)"
        />
        <div class="grid grid-cols-6 gap-4 mt-4">
          <div *ngFor="let pokemon of filteredPokemons()" 
               class="bg-white p-4 rounded-lg border border-gray-200">
            <p class="text-center capitalize">{{ pokemon.name }}</p>
          </div>
        </div>
      </div>
    </div>
    

    Comparativa: Cómo se hacía antes con RxJS

    A continuación, vemos cómo implementar el mismo comportamiento usando RxJS y el enfoque clásico basado en observables:

    // Importamos decoradores y operadores necesarios
    import { Component, OnInit } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { BehaviorSubject, combineLatest, map } from 'rxjs';
    
    @Component({
      selector: 'app-pokemon-search-rxjs',
      standalone: true,
      templateUrl: './pokemon-search.component.html',
    })
    export class PokemonSearchRxjsComponent implements OnInit {
      // URL de la API
      private apiUrl = 'https://pokeapi.co/api/v2/pokemon?limit=151';
    
      // Observable que representa el término de búsqueda
      private searchTerm$ = new BehaviorSubject<string>('');
    
      // Observable que obtiene todos los Pokémon desde la API
      private allPokemons$ = this.http.get<any>(this.apiUrl).pipe(
        map(res => res.results)
      );
    
      // Combina ambos observables y filtra los resultados según el término
      filteredPokemons$ = combineLatest([this.allPokemons$, this.searchTerm$]).pipe(
        map(([pokemons, term]) =>
          pokemons.filter((p: any) =>
            p.name.toLowerCase().includes(term.toLowerCase())
          )
        )
      );
    
      constructor(private http: HttpClient) {}
    
      // Método de Angular, aunque no es necesario en este ejemplo
      ngOnInit(): void {}
    
      // Función para actualizar el término de búsqueda
      updateSearch(term: string) {
        this.searchTerm$.next(term);
      }
    }

    Comparación línea por línea

    Angular Signals RxJS clásico
    signal('') new BehaviorSubject('')
    computed(() => ...) combineLatest([...]).pipe(map(...))
    effect(() => ...) subscribe(...) (si hiciera falta)
    .set() .next()
    Reactividad declarativa integrada Reactividad con suscripción explícita
    Sin necesidad de unsubscribe Debes cuidar las suscripciones manuales

    Ventajas de usar Signals

    • No necesitas suscripciones ni unsubscribe.
    • Menos boilerplate.
    • Integración directa con Angular y detección de cambios optimizada.
    • Código más declarativo y legible.

    Conclusión

    Angular 19 marca una evolución en la forma de trabajar con reactividad. Signals y Effects ofrecen una experiencia más directa, clara y moderna, ideal para la mayoría de los casos donde antes usábamos RxJS por obligación. Aunque RxJS sigue siendo útil en escenarios complejos, Signals resuelven el 80% de los casos comunes con menos código y más claridad.

    En futuros posts podríamos combinar Signals con rutas, inputs y servicios para crear apps más complejas, o explorar cómo coexistir con código existente basado en RxJS.

  • Cómo construir un Multiselect Dropdown en 4 Frameworks modernos

    Cómo construir un Multiselect Dropdown en 4 Frameworks modernos

    Vamos a construir un componente multiselect desde cero usando React y Tailwind CSS, que nos permitirá seleccionar múltiples opciones desde un dropdown y mostrar en consola las opciones seleccionadas. Empezaremos con React ya que es lo que más hemos visto hasta ahora.

    Paso 1: Crear la estructura base del componente

    Primero vamos a crear el layout base del componente usando sólo HTML y clases de Tailwind. Esto nos permite enfocarnos primero en el diseño sin preocuparnos por la lógica todavía.

    Creamos un archivo: MultiSelectDropdown.tsx Y pegamos esta estructura:

    export default function MultiSelectDropdown() {
      return (
        <div className="w-60 m-auto">
          <button
            type="button"
            className="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
          >
            Dropdown button
            <svg
              className="w-2.5 h-2.5 ml-3"
              viewBox="0 0 10 6"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M1 1L5 5L9 1"
                stroke="currentColor"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
            </svg>
          </button>
    
          {/* Dropdown visible por ahora */}
          <div className="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm dark:bg-gray-700">
            <ul className="p-3 space-y-3 text-sm text-gray-700 dark:text-gray-200">
              <li>
                <label className="flex items-center gap-2">
                  <input type="checkbox" />
                  React
                </label>
              </li>
              <li>
                <label className="flex items-center gap-2">
                  <input type="checkbox" />
                  Vue
                </label>
              </li>
            </ul>
          </div>
        </div>
      );
    }
    

    ¿Qué hicimos aquí?

    • Creamos un contenedor de ancho fijo centrado.

    • Agregamos un botón con estilos de Tailwind (bg-blue-700, rounded-lg, etc.).

    • Insertamos una lista de opciones de ejemplo.

    • Todo el diseño es estático todavía, sin funcionalidad.

    Paso 2: Manejar la apertura y cierre del dropdown

    Ahora vamos a hacer que el dropdown se oculte o muestre cuando hacemos clic en el botón.

    import { useState } from "react";
    
    export default function MultiSelectDropdown() {
      const [isOpen, setIsOpen] = useState(false);
    
      return (
        <div className="w-60 m-auto">
          <button
            type="button"
            onClick={() => setIsOpen((prev) => !prev)}
            className="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
          >
            Dropdown button
            <svg
              className="w-2.5 h-2.5 ml-3"
              viewBox="0 0 10 6"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M1 1L5 5L9 1"
                stroke="currentColor"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
            </svg>
          </button>
    
          {isOpen && (
            <div className="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm dark:bg-gray-700">
              <ul className="p-3 space-y-3 text-sm text-gray-700 dark:text-gray-200">
                <li>
                  <label className="flex items-center gap-2">
                    <input type="checkbox" />
                    React
                  </label>
                </li>
                <li>
                  <label className="flex items-center gap-2">
                    <input type="checkbox" />
                    Vue
                  </label>
                </li>
              </ul>
            </div>
          )}
        </div>
      );
    }
    

    ¿Qué aprendemos aquí?

    • React usa useState para manejar el estado de apertura del menú.

    • Con {isOpen && (...)} mostramos u ocultamos el dropdown de forma condicional.

    • Esto ya nos permite ver el menú al hacer clic, y ocultarlo al hacer clic de nuevo.

    Paso 3: Crear opciones dinámicas

    Ahora definimos una lista de opciones para renderizarlas dinámicamente. Así podemos cambiar fácilmente las opciones más adelante sin repetir código (Por el momento dentro del componente pero se puede fácilmente pasar como prop).

    const options = [
      { label: "React", value: "react" },
      { label: "Vue", value: "vue" },
      { label: "Svelte", value: "svelte" },
      { label: "Solid", value: "solid" },
      { label: "Angular", value: "angular" },
    ];
    

    Y en lugar de escribir <li> manualmente, iteramos con .map():

    <ul className="p-3 space-y-3 text-sm text-gray-700 dark:text-gray-200">
      {options.map((option) => (
        <li key={option.value}>
          <label className="flex items-center gap-2">
            <input type="checkbox" />
            {option.label}
          </label>
        </li>
      ))}
    </ul>
    

    Paso 4: Guardar y manejar las opciones seleccionadas

    Ahora implementamos la lógica para guardar las selecciones. Creamos una nueva variable de estado llamada selected que guardará un array con los valores seleccionados.

    Además, queremos que cada vez que se seleccione o deseleccione algo, se haga un console.log() del array actualizado. Para evitar logs duplicados, lo haremos con useEffect.

    import { useState, useEffect } from "react";
    
    export default function MultiSelectDropdown() {
      const [isOpen, setIsOpen] = useState(false);
      const [selected, setSelected] = useState<string[]>([]);
    
      // Este efecto imprime en consola cada vez que cambia la selección
      useEffect(() => {
        console.log("Seleccionadas:", selected);
      }, [selected]);
    
      const handleChange = (value: string) => {
        setSelected((prev) =>
          prev.includes(value)
            ? prev.filter((v) => v !== value)
            : [...prev, value]
        );
      };
    
      const options = [
        { label: "React", value: "react" },
        { label: "Vue", value: "vue" },
        { label: "Svelte", value: "svelte" },
        { label: "Solid", value: "solid" },
        { label: "Angular", value: "angular" },
      ];
    
      return (
        <div className="w-60 m-auto">
          <button
            onClick={() => setIsOpen((prev) => !prev)}
            type="button"
            className="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
          >
            Dropdown button
            <svg className="w-2.5 h-2.5 ml-3" /* ... */ />
          </button>
    
          {isOpen && (
            <div className="z-10 w-full mt-2 block bg-white rounded-lg shadow-sm">
              <ul className="p-3 space-y-3 text-sm text-gray-700">
                {options.map((option) => (
                  <li key={option.value}>
                    <label className="flex items-center gap-2">
                      <input
                        type="checkbox"
                        checked={selected.includes(option.value)}
                        onChange={() => handleChange(option.value)}
                      />
                      {option.label}
                    </label>
                  </li>
                ))}
              </ul>
            </div>
          )}
        </div>
      );
    }
    

    Resultado

    Ya tenemos un componente 100% funcional en React:

    • Visualmente estilizado con Tailwind.

    • Dropdown desplegable al clic.

    • Permite seleccionar y deseleccionar múltiples opciones.

    • Imprime en consola las opciones seleccionadas sin duplicados.

     

    Multiselect Dropdown en Vue 3 + Tailwind + TypeScript

    Aquí replicamos el mismo componente de React, pero usando Vue 3, Composition API, TypeScript y Tailwind. Lo haremos desde cero, en pasos simples y comentados.

    • Mostrará un botón para desplegar un menú con opciones.

    • Permitirá seleccionar múltiples opciones (checkboxes).

    • Al seleccionar/deseleccionar, actualizará un array reactivo.

    • Imprimirá en consola las opciones seleccionadas (sin duplicados).

    • Usará Tailwind CSS para estilos.

     

    Paso 1: Crear la estructura del componente

    Creamos el archivo:

    src/components/MultiSelectDropdown.vue
    

    Y comenzamos con la estructura mínima:

    <template>
      <div class="w-60 m-auto">
        <button
          type="button"
          class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
        >
          Dropdown button
          <svg
            class="w-2.5 h-2.5 ml-3"
            viewBox="0 0 10 6"
            fill="none"
            xmlns="http://www.w3.org/2000/svg"
          >
            <path
              d="M1 1L5 5L9 1"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            />
          </svg>
        </button>
    
        <div class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm">
          <ul class="p-3 space-y-3 text-sm text-gray-700">
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                React
              </label>
            </li>
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                Vue
              </label>
            </li>
          </ul>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    // Lógica vendrá después
    </script>
    

    ¿Qué hicimos aquí?

    • Creamos la estructura visual: un botón y un dropdown con dos opciones fijas.

    • Usamos Tailwind para el diseño (botón azul, checkbox alineado).

    • No hay interactividad aún: ni apertura/cierre, ni selección.

    Este paso sirve para tener el esqueleto visual listo antes de agregar la lógica.

    Paso 2: Mostrar/Ocultar el dropdown con ref()

    Queremos que el dropdown solo se muestre cuando hagamos clic en el botón. Para eso:

    • Creamos una variable reactiva llamada isOpen

    • Usamos v-if="isOpen" para mostrar el menú

    • Alternamos el valor con un método toggleDropdown()

    Actualizamos el componente así:

    <template>
      <div class="w-60 m-auto">
        <!-- Botón que abre/cierra el dropdown -->
        <button
          type="button"
          @click="toggleDropdown"
          class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
        >
          Dropdown button
          <svg
            class="w-2.5 h-2.5 ml-3"
            viewBox="0 0 10 6"
            fill="none"
            xmlns="http://www.w3.org/2000/svg"
          >
            <path
              d="M1 1L5 5L9 1"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            />
          </svg>
        </button>
    
        <!-- Mostramos el dropdown solo si isOpen es true -->
        <div
          v-if="isOpen"
          class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm"
        >
          <ul class="p-3 space-y-3 text-sm text-gray-700">
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                React
              </label>
            </li>
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                Vue
              </label>
            </li>
          </ul>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    import { ref } from 'vue'
    
    const isOpen = ref(false)
    
    const toggleDropdown = () => {
      isOpen.value = !isOpen.value
    }
    </script>
    

    ¿Qué aprendimos aquí?

    • ref(false) crea una variable reactiva en Vue.

    • Usamos v-if="isOpen" para mostrar el menú condicionalmente.

    • Al hacer clic en el botón, toggleDropdown() invierte su valor (true / false).

    Ya tenemos la interacción básica lista. ¡Se ve como un dropdown real!

    Paso 3: Mostrar opciones desde un array con v-for

    Hasta ahora, las opciones están “quemadas” en el HTML. Vamos a moverlas a un array para que el componente sea dinámico y reusable.

    1. Creamos un array de opciones en el <script setup>

    Usamos TypeScript para tipar las opciones:

    interface Option {
      label: string
      value: string
    }
    
    const options: Option[] = [
      { label: 'React', value: 'react' },
      { label: 'Vue', value: 'vue' },
      { label: 'Svelte', value: 'svelte' },
      { label: 'Solid', value: 'solid' },
      { label: 'Angular', value: 'angular' }
    ]
    

    2. Reemplazamos los <li> fijos por un v-for

    Actualizamos el template:

    <ul class="p-3 space-y-3 text-sm text-gray-700">
      <li v-for="option in options" :key="option.value">
        <label class="flex items-center gap-2">
          <input type="checkbox" />
          {{ option.label }}
        </label>
      </li>
    </ul>
    

    ¿Qué logramos aquí?

    • El componente ahora usa un array tipado con Option[], lo que facilita reusarlo.

    • Con v-for, renderizamos tantas opciones como queramos sin duplicar código.

    • Vue se encarga de mantener la eficiencia del DOM con :key="option.value".

    Paso 4: Manejar selección múltiple (checkboxes)

    Ahora queremos que:

    • Cada checkbox represente una opción seleccionable.

    • Las opciones seleccionadas se guarden en un array selected.

    • Si ya está seleccionada, se deseleccione (toggle).

    • Se imprima en consola cada vez que el array cambie.

    1. Creamos el estado selected

    En el <script setup>:

    import { ref, watch } from 'vue'
    
    const selected = ref<string[]>([])
    

    2. Creamos una función para agregar o quitar selecciones

    const toggleSelection = (value: string) => {
      if (selected.value.includes(value)) {
        selected.value = selected.value.filter(v => v !== value)
      } else {
        selected.value.push(value)
      }
    }
    

    3. Escuchamos los cambios con watch (como useEffect en React)

    Usamos :checked y @change para enlazar con el estado:

    <input
      type="checkbox"
      :checked="selected.includes(option.value)"
      @change="toggleSelection(option.value)"
    />
    

    Resultado

    Ahora el componente:

    • Permite seleccionar múltiples opciones.

    • Guarda las seleccionadas en un array.

    • Hace console.log() al cambiar la selección.

     

    Multiselect Dropdown en Svelte + Tailwind + TypeScript

    Paso 1: Estructura HTML + Tailwind base

    Vamos a comenzar por construir solo la estructura visual estática del componente, sin lógica aún. El objetivo es ver cómo se verá el dropdown, el botón y las opciones con checkboxes, usando únicamente HTML y Tailwind CSS.

    Código inicial: MultiSelectDropdown.svelte

    <script lang="ts">
      // lógica vendrá después
    </script>
    
    <div class="w-60 m-auto">
      <!-- Botón para abrir el dropdown -->
      <button
        type="button"
        class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
      >
        Dropdown button
        <svg
          class="w-2.5 h-2.5 ml-3"
          viewBox="0 0 10 6"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M1 1L5 5L9 1"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
        </svg>
      </button>
    
      <!-- Dropdown (visible todo el tiempo por ahora) -->
      <div
        class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm"
      >
        <ul class="p-3 space-y-3 text-sm text-gray-700">
          <li>
            <label class="flex items-center gap-2">
              <input type="checkbox" />
              React
            </label>
          </li>
          <li>
            <label class="flex items-center gap-2">
              <input type="checkbox" />
              Vue
            </label>
          </li>
        </ul>
      </div>
    </div>
    

    ¿Qué tenemos hasta ahora?

    • Un botón con diseño responsivo (w-full, bg-blue-700, hover, rounded-lg).

    • Un ícono de flecha hacia abajo (svg).

    • Un menú tipo dropdown con dos opciones de ejemplo (React, Vue) como <li>.

    • Cada opción es un <label> que contiene un checkbox y un texto.

    Paso 2: Mostrar/Ocultar el dropdown con una variable reactiva

    Ahora vamos a hacer que el dropdown se abra y cierre al hacer clic en el botón, usando reactividad en Svelte con let.

    ¿Cómo lo haremos?

    • Creamos una variable isOpen con let

    • Alternamos su valor al hacer clic en el botón

    • Mostramos el dropdown solo si isOpen === true usando {#if}

    Código actualizado

    <script lang="ts">
      let isOpen = false;
    
      function toggleDropdown() {
        isOpen = !isOpen;
      }
    </script>
    
    <div class="w-60 m-auto">
      <!-- Botón con evento on:click -->
      <button
        type="button"
        on:click={toggleDropdown}
        class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
      >
        Dropdown button
        <svg
          class="w-2.5 h-2.5 ml-3"
          viewBox="0 0 10 6"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M1 1L5 5L9 1"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
        </svg>
      </button>
    
      <!-- Mostrar dropdown solo si isOpen es true -->
      {#if isOpen}
        <div
          class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm"
        >
          <ul class="p-3 space-y-3 text-sm text-gray-700">
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                React
              </label>
            </li>
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                Vue
              </label>
            </li>
          </ul>
        </div>
      {/if}
    </div>
    

    ¿Qué aprendimos aquí?

    • En Svelte, let crea variables reactivas automáticamente.

    • on:click={toggleDropdown} es la forma de escuchar eventos.

    • {#if ...} es como v-if en Vue o un render condicional en React ({isOpen && (...)}).

     

    Paso 3: Mostrar las opciones dinámicamente con {#each}

    Hasta ahora, las opciones (React, Vue) están escritas directamente en el HTML. Ahora vamos a moverlas a un array tipado para que el componente sea dinámico y escalable.

    1. Creamos el array de opciones en el <script>

    interface Option {
      label: string;
      value: string;
    }
    
    const options: Option[] = [
      { label: 'React', value: 'react' },
      { label: 'Vue', value: 'vue' },
      { label: 'Svelte', value: 'svelte' },
      { label: 'Solid', value: 'solid' },
      { label: 'Angular', value: 'angular' },
    ];
    

    Option es una interfaz que usamos para tipar las opciones. Esto es posible gracias a que activaste TypeScript

    2. Reemplazamos los <li> fijos por {#each}

    <ul class="p-3 space-y-3 text-sm text-gray-700">
      {#each options as option (option.value)}
        <li>
          <label class="flex items-center gap-2">
            <input type="checkbox" />
            {option.label}
          </label>
        </li>
      {/each}
    </ul>
    

    ¿Qué logramos?

    • Las opciones ya no están duplicadas manualmente.

    • Podemos fácilmente agregar, quitar o pasar las opciones como props después.

    • Cada opción se representa con un checkbox y su label.

    Paso 4: Manejar selección múltiple y mostrarla en consola

    Queremos que el componente:

    1. Permita seleccionar múltiples opciones (con checkboxes).

    2. Guarde las opciones seleccionadas en un array.

    3. Haga console.log() del array cada vez que cambie.

    ¿Cómo lo haremos?

    • Creamos un array reactivo selected: string[].

    • Usamos bind:checked para vincular cada checkbox con el estado.

    • O mejor: manejamos el on:change manualmente, como hicimos en Vue y React (para control total).

    • Imprimimos con $: console.log(...) que es como watch o useEffect.

    1. Creamos selected y toggleSelection()

    let selected: string[] = [];
    
    function toggleSelection(value: string) {
      if (selected.includes(value)) {
        selected = selected.filter((v) => v !== value);
      } else {
        selected = [...selected, value];
      }
    }
    
    // log reactivo
    $: console.log('Seleccionadas:', selected);
    

    2. Actualizamos los checkboxes

    En cada input, usamos:

    <input
      type="checkbox"
      checked={selected.includes(option.value)}
      on:change={() => toggleSelection(option.value)}
    />
    

    ¿Qué logramos?

    • El dropdown ya permite selección múltiple.

    • Los checkboxes reflejan el estado real.

    • Cada cambio en selected hace un console.log() sin duplicados.

    • Comportamiento igual al de React y Vue

    Multiselect Dropdown en Angular + Tailwind + TypeScript

    Paso 1: Estructura HTML + Tailwind base

    Primero, creamos solo la estructura visual del componente, sin lógica aún. Esto nos permite ver el diseño del botón y el menú desplegable.

    1. Crear el componente

    Desde la terminal, en el proyecto Angular:

    ng generate component components/multi-select-dropdown
    

    Esto creará 4 archivos dentro de src/app/components/multi-select-dropdown:

    • multi-select-dropdown.component.ts

    • multi-select-dropdown.component.html

    • multi-select-dropdown.component.css

    • multi-select-dropdown.component.spec.ts

     

    2. Usar el componente en app.component.html

    Edita el archivo src/app/app.component.html y agrega:

    <app-multi-select-dropdown></app-multi-select-dropdown>
    

    3. Estructura HTML base en multi-select-dropdown.component.html

    Pegamos la estructura visual básica con Tailwind:

    <div class="w-60 m-auto">
      <!-- Botón que abre el dropdown -->
      <button
        type="button"
        class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
      >
        Dropdown button
        <svg
          class="w-2.5 h-2.5 ml-3"
          viewBox="0 0 10 6"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M1 1L5 5L9 1"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
        </svg>
      </button>
    
      <!-- Dropdown visible por ahora (sin lógica) -->
      <div
        class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm"
      >
        <ul class="p-3 space-y-3 text-sm text-gray-700">
          <li>
            <label class="flex items-center gap-2">
              <input type="checkbox" />
              React
            </label>
          </li>
          <li>
            <label class="flex items-center gap-2">
              <input type="checkbox" />
              Vue
            </label>
          </li>
        </ul>
      </div>
    </div>
    

    Resultado esperado

    Si recargas http://localhost:4200, deberías ver:

    • Un botón azul que dice “Dropdown button”

    • Un dropdown con dos opciones (React y Vue)

    • Estilo limpio con Tailwind

    Paso 2: Mostrar/Ocultar el dropdown con una variable en el componente

    Queremos que el dropdown:

    • Se muestre al hacer clic en el botón.

    • Se oculte al volver a hacer clic.

    ¿Cómo lo haremos?

    1. Creamos una propiedad booleana isOpen en el .ts.

    2. Hacemos toggle con un método toggleDropdown().

    3. En el HTML, usamos *ngIf="isOpen" para mostrar/ocultar el menú.

    1. Agregar lógica en el archivo .ts

    Edita multi-select-dropdown.component.ts:

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-multi-select-dropdown',
      templateUrl: './multi-select-dropdown.component.html',
      styleUrls: ['./multi-select-dropdown.component.css'],
    })
    export class MultiSelectDropdownComponent {
      isOpen = false;
    
      toggleDropdown(): void {
        this.isOpen = !this.isOpen;
      }
    }
    

    2. Conectar el botón al método

    Edita multi-select-dropdown.component.html:

    <!-- Botón con (click) -->
    <button
      type="button"
      (click)="toggleDropdown()"
      class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
    >
      Dropdown button
      <svg
        class="w-2.5 h-2.5 ml-3"
        viewBox="0 0 10 6"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
      >
        <path
          d="M1 1L5 5L9 1"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        />
      </svg>
    </button>
    

    3. Mostrar el dropdown condicionalmente

    Reemplaza el div del dropdown así:

    <div
      *ngIf="isOpen"
      class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm"
    >
      <ul class="p-3 space-y-3 text-sm text-gray-700">
        <li>
          <label class="flex items-center gap-2">
            <input type="checkbox" />
            React
          </label>
        </li>
        <li>
          <label class="flex items-center gap-2">
            <input type="checkbox" />
            Vue
          </label>
        </li>
      </ul>
    </div>
    

    Resultado

    Ahora al hacer clic en el botón:

    • El menú aparece y desaparece.

    • El estado lo maneja isOpen con un simple toggle.

    Paso 3: Mostrar las opciones dinámicamente desde un array

    Hasta ahora, las opciones (React, Vue) están escritas directamente en el HTML. Vamos a moverlas a un array tipado en TypeScript y usar *ngFor para renderizarlas dinámicamente.

    1. Declarar la interfaz y el array de opciones

    Edita multi-select-dropdown.component.ts:

    interface Option {
      label: string;
      value: string;
    }
    
    export class MultiSelectDropdownComponent {
      isOpen = false;
    
      options: Option[] = [
        { label: 'React', value: 'react' },
        { label: 'Vue', value: 'vue' },
        { label: 'Svelte', value: 'svelte' },
        { label: 'Solid', value: 'solid' },
        { label: 'Angular', value: 'angular' }
      ];
    
      toggleDropdown(): void {
        this.isOpen = !this.isOpen;
      }
    }
    

    2. Usar *ngFor para renderizar el listado en el HTML

    En multi-select-dropdown.component.html, reemplaza los <li> fijos por:

    <ul class="p-3 space-y-3 text-sm text-gray-700">
      <li *ngFor="let option of options">
        <label class="flex items-center gap-2">
          <input type="checkbox" />
          {{ option.label }}
        </label>
      </li>
    </ul>
    

    ¿Qué logramos?

    • Las opciones ahora se gestionan desde el .ts, de forma escalable.

    • Podemos cambiar las opciones fácilmente o cargarlas desde un API más adelante.

    • El template está más limpio, sin duplicación.

    Paso 4: Manejar selección múltiple con checkboxes

    Queremos que:

    1. Cada vez que se seleccione o deseleccione una opción, actualicemos un array.

    2. El array contenga los value de las opciones seleccionadas.

    3. Usemos console.log() para mostrar el estado en tiempo real.

    ¿Cómo lo haremos?

    • Creamos selected: string[] = [] en el componente.

    • Usamos (change) en cada checkbox para manejar la selección.

    • Usamos [checked] para marcar el estado del checkbox.

    • Imprimimos en consola cada vez que cambia la lista.

    1. Agrega el estado y función en el .ts

    selected: string[] = [];
    
    toggleSelection(value: string): void {
      if (this.selected.includes(value)) {
        this.selected = this.selected.filter(v => v !== value);
      } else {
        this.selected.push(value);
      }
    
      console.log('Seleccionadas:', this.selected);
    }
    

    2. Actualiza el input en el HTML

    Modifica el input dentro del *ngFor así:

    <input
      type="checkbox"
      [checked]="selected.includes(option.value)"
      (change)="toggleSelection(option.value)"
    />
    

    Resultado

    Ahora tu componente:

    • Permite seleccionar múltiples opciones.

    • Guarda la selección actualizada en this.selected.

    • Muestra el array actualizado en consola cada vez que haces clic.

     

    Cierre del post:

    🎯 ¿Qué hicimos?

    Construimos el mismo componente multiselect dropdown visualmente consistente en:

    Framework Enfoque Lenguaje Estado JSX / Template Observaciones
    React Hooks (useState, useEffect) TypeScript useState JSX Familiar y flexible. Control explícito.
    Vue 3 Composition API + ref() + watch TypeScript ref, watch Plantilla declarativa Reactividad clara y directa. Fácil binding.
    Svelte Variables reactivas (let, $:) TypeScript Local vars + auto-reactividad Template HTML puro Sintaxis más limpia y mínima. Muy ergonómico.
    Angular Component Class + *ngIf + (change) TypeScript Clases + propiedades Template estructurado Verboso pero robusto. Ideal en equipos grandes.

    ¿Qué aprendimos?

    • Todos los frameworks modernos permiten construir componentes dinámicos y accesibles.

    • Aunque el resultado visual es el mismo, cada ecosistema tiene su propio “ritmo”:

      • React son ideales si te gusta el control imperativo y JSX.

      • Vue y Svelte son más declarativos y expresivos.

      • Angular prioriza estructura, reglas y escalabilidad.

     

    ¿Cuál fue más simple?

    • En cuanto a cantidad de código y claridad inmediata, Svelte y Vue 3 se sintieron más livianos.

    • React ofrecieron control fino y granularidad, ideales para lógica compleja.

    • Angular fue el más verboso, pero también el más estricto y mantenible para proyectos grandes.

    ¿Dónde usar este patrón?

    Un componente como este es útil en:

    • Formularios de filtro avanzados

    • Paneles de administración

    • Dashboards

    • Apps móviles con diseño adaptativo

    Conclusión

    Construir un componente funcional, accesible y visualmente uniforme en diferentes frameworks es una excelente forma de:

    • Aprender sus fortalezas y diferencias

    • Comparar enfoques reactivos

    • Evaluar la curva de entrada y el esfuerzo de mantenimiento

    Como desarrolladores, saber trabajar en más de un entorno nos hace más versátiles y estratégicos.

  • Los mejores UI kits gratuitos con Tailwind CSS

    Los mejores UI kits gratuitos con Tailwind CSS

    Aquí tienes un resumen rápido de las 10 mejores opciones:

    • Flowbite: Más de 400 componentes, compatible con Laravel y Vue.js
    • DaisyUI: Más de 50 componentes, sintaxis sencilla, más de 15,000 estrellas en GitHub
    • Preline UI: Más de 300 componentes, 160 páginas de inicio, soporte para varios frameworks
    • HyperUI: Más de 42 componentes enfocados en marketing, eCommerce y aplicaciones
    • TailGrids: Más de 300 componentes, compatible con React, Vue y Angular
    • Headless UI: Componentes accesibles sin estilos para control total
    • Tailblocks: Componentes prediseñados para ensamblado rápido de páginas
    • Meraki UI: 108 componentes, soporte para modo oscuro y RTL (idiomas de derecha a izquierda)
    • Mamba UI: Mezcla de componentes gratuitos y de pago, diseños modernos
    • Tailwind Starter Kit: Elementos HTML y componentes dinámicos para múltiples frameworks

    Comparación rápida:

     

    Librería Componentes Estrellas GitHub Soporte de Framework Personalización
    Flowbite 400+ 1,076 Alpine.js, Laravel, Vue.js Alta
    DaisyUI 56 28,494 Cualquier compatible con Tailwind Alta
    Preline UI 300+ 3,200 React, Next.js, Svelte, Remix Alta
    HyperUI 42+ 6,949 Cualquier compatible con Tailwind Moderada
    TailGrids 300+ N/D React, Vue, Angular Alta
    Headless UI Sin estilo 17,300 React, Vue Alta
    Tailblocks Variado N/D Cualquier compatible con Tailwind Limitada
    Meraki UI 108 N/D Cualquier compatible con Tailwind Alta
    Mamba UI 150+ 300 Angular, React, Svelte, Vue Moderada
    Tailwind Starter Kit 120+ 5,000 React, Vue, Angular Alta

    Elige basándote en tus necesidades:

    Cantidad de componentes, compatibilidad con frameworks y nivel de personalización.
    Para muchos componentes, prueba Flowbite o Preline UI.
    ¿Buscas gran comunidad? DaisyUI es la opción.
    ¿Necesitas accesibilidad? Explora Headless UI.

    ¿Cómo elegimos estas herramientas?

    Tomamos en cuenta:

    1. Gratuidad: 100% gratuitas.

    2. Cantidad de componentes: como Flowbite (400+) o TailGrids (300+).

    3. Compatibilidad con frameworks: Flowbite trabaja con Laravel, React y Vue.

    4. Facilidad de uso: DaisyUI destaca con su sintaxis simple.

    5. Personalización: Meraki UI ofrece flexibilidad con Flexbox y CSS Grid.

    6. Actualización: Todos funcionan con la última versión de Tailwind CSS.

    7. Apoyo comunitario: Preferimos librerías open-source activas.

    Los 10 mejores kits gratuitos de Tailwind CSS

    1. Flowbite

    • 400+ componentes y elementos interactivos.

    • Copiar y pegar directamente en tu proyecto.

    • Compatible con Laravel y Vue.js (para React usar Flowbite React).

    Ejemplo de botón Flowbite:

    <button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Default</button>

    2. DaisyUI

    • Más de 50 componentes.

    • 15,000+ estrellas en GitHub.

    • Más de 2 millones de instalaciones en NPM.

    • Sintaxis limpia y temas personalizables.

    • Totalmente responsive.

    • Algunas críticas por colores predeterminados muy brillantes.

    3. Preline UI

    • Más de 300 componentes y 160 páginas de inicio.

    • Compatible con React, Next.js, Svelte, Remix, y más.

    • Incluye kit de Figma.

    4. HyperUI

    • Más de 42 componentes.

    • Enfocado en marketing, eCommerce y UIs de aplicaciones.

    • Ideal para prototipar rápidamente.

    5. TailGrids

    • 300+ componentes.

    • Compatible con React, Vue y Angular.

    • Documentación extensa con vistas en vivo.

    6. Headless UI

    • Componentes accesibles y sin estilos.

    • Diseñado para control total del estilo.

    • Integración perfecta con Tailwind CSS.

    7. Tailblocks

    • Componentes prediseñados para montar páginas rápido.

    • Diseño limpio y moderno.

    • Personalización limitada.

    8. Meraki UI

    • 108 componentes.

    • Soporta RTL y modo oscuro.

    • Uso de Flexbox y CSS Grid.

    9. Mamba UI

    • Más de 150 componentes (algunos gratuitos, otros pagos).

    • Código en HTML o JSX.

    • Diseños modernos y atractivos.

    10. Tailwind Starter Kit

    • Plantillas básicas de HTML.

    • Componentes dinámicos para React, Vue y Angular.

    • Código abierto y gratuito.

    Comparativa final

     

    Librería Componentes Estrellas GitHub Frameworks Soportados Personalización
    Flowbite 400+ 1,076 Alpine.js, Laravel, Vue.js Alta
    DaisyUI 56 28,494 Cualquiera compatible Alta
    Preline UI 300+ 3,200 React, Next.js, Svelte, Remix Alta
    HyperUI 42+ 6,949 Cualquiera compatible Moderada
    TailGrids 300+ N/D React, Vue, Angular Alta
    Headless UI Sin estilo 17,300 React, Vue Alta
    Tailblocks Variado N/D Cualquiera compatible Limitada
    Meraki UI 108 N/D Cualquiera compatible Alta
    Mamba UI 150+ 300 Angular, React, Svelte, Vue Moderada
    Tailwind Starter Kit 120+ 5,000 React, Vue, Angular Alta

    ¿Qué significa esto para ti?

    • ¿Necesitas muchos componentes? → Flowbite o Preline UI.

    • ¿Quieres apoyo de comunidad? → DaisyUI.

    • ¿Buscas accesibilidad en React/Vue? → Headless UI.

    • ¿Máxima personalización? → Casi todos ofrecen alta, excepto Tailblocks.

    Extras:

    • Meraki UI: Soporte RTL y modo oscuro.

    • Headless UI: Accesibilidad enfocada.

    • HyperUI: Especial para marketing y comercio electrónico.

    Cómo elegir:

    • ¿Cuántos componentes necesitas?

    • ¿Qué framework estás usando?

    • ¿Cuánto nivel de personalización deseas?

    • ¿Requieres características especiales (accesibilidad, RTL)?

    ✅ ¿Accesibilidad para React? → Headless UI
    ✅ ¿Muchos componentes pre-estilizados? → Flowbite o Preline UI

    Cierre

    Elegir el kit de UI adecuado en Tailwind CSS puede definir el éxito de tu proyecto. Considera:

    – Cantidad de componentes:

    • Flowbite: 400+

    • Tailwind Elements: 500+

    • TailGrids: 300+

    Para proyectos pequeños, DaisyUI o HyperUI son ideales.

    – Compatibilidad con frameworks:

    • Headless UI: React, Vue

    • Flowbite: Alpine.js, Laravel, Vue.js

    • TailGrids: React, Vue, Angular

    – Personalización:

    • Casi todas ofrecen alta personalización, excepto Tailblocks.

    – Características únicas:

    • Meraki UI: Idiomas RTL y modo oscuro

    • Headless UI: Accesibilidad

    • HyperUI: Componentes de marketing/eCommerce