Entendiendo los componentes de Laravel Livewire

- Andrés Cruz - EN In english

Video thumbnail

Qué es un componente Livewire y cómo funciona realmente

Cuando empecé a trabajar con Livewire, lo primero que me llamó la atención fue que se comporta como una SPA… pero sin escribir JavaScript. En cuanto entiendes su arquitectura, se vuelve evidente: Livewire actualiza solo lo que cambia. Nada más.

Un componente Livewire es básicamente la combinación de dos piezas:

  • Una clase PHP que maneja el estado (propiedades y métodos)
  • Una vista Blade que representa el UI de ese componente

Aunque en Volt ambas piezas están unidas en un solo archivo.

La “magia” está en la sincronización. Cada vez que una propiedad cambia —ya sea porque el usuario hace clic, escribe algo, o el servidor ejecuta alguna acción— Livewire hace una única petición al backend, procesa el cambio y… redibuja solo ese componente.

En una de mis primeras pruebas, al cambiar un select de idioma, lo confirmé mirando la pestaña Network del navegador: aparece solo una petición. Nada más. Y lo mejor: el layout seguía intacto. Eso te muestra claramente que no estás recargando toda la página, sino solo la parte activa. Exactamente lo que hace una SPA.

El concepto clave: estado + vista sincronizados

El flujo es así:

  • El usuario hace algo (click, submit, escribe, selecciona).
  • Livewire envía un snapshot del estado al servidor.
  • El servidor ejecuta métodos o actualiza propiedades.
  • Livewire devuelve solo el HTML que cambió.
  • El DOM se actualiza de forma inteligente (morphing).

Por qué Livewire se comporta como una SPA en Laravel

Livewire usa un algoritmo llamado DOM morphing, que compara el HTML viejo y el nuevo, y actualiza solo las diferencias. No sustituye todo el DOM, no recarga la página ni recompone el layout. Solo toca lo que cambió.

Esta eficiencia se nota especialmente en formularios, sistemas de filtros, paneles de usuario y dashboards.

Ejemplo de funcionamiento de componente

Por aquí quiero mostrarte algo que me pareció muy ilustrativo, para que entiendas exactamente cómo funcionan los componentes en Livewire y por qué realmente es una aplicación de tipo SPA (Single Page Application). Esto significa que no recarga toda la página, y no solo quedarnos con las características de un proyecto en Livewire que presentamos antes sino únicamente la parte que está cambiando, es decir, el componente con el cual estás trabajando. Al menos, claro, de manera básica.

Si tú haces uso del sistema de listeners para comunicar al padre, al abuelo, al bisabuelo, a la tía o al que sea, obviamente la cosa cambia. Pero me refiero al flujo normal, por así decirlo.

Tenemos un selector para el idioma. Fíjate que si cambio a “Español”, todo aparece en español:

<?php

namespace App\Livewire\User;

use Livewire\Attributes\Layout;
use Livewire\Component;

use Illuminate\Support\Facades\App;

#[Layout('layouts.web')]
class UserProfile extends Component
{

    public $language;

    public function render()
    {
        if (!isset($this->language)) {
            // al cargar el componente, este codigo se ejecuta al language NO estar seleccionado 
            // por el usuario
            $this->language = session('locale') ?? App::getLocale();
        } else {
            // se ejecuta desde la vista asociada por el .live
            session(['locale' => $this->language]);
            App::setLocale($this->language);
        }
        
        return view('livewire.user.user-profile');
    }
}

Y la vista:

<div class="max-w-md m-auto mycard">
    <div class="grid grid-cols-1 gap-2 my-3 mycard-body">
        <flux:input :label="__('Email')" value="{{ auth()->user()->email }}" disabled readonly/>

        <flux:input :label="__('Name')" value="{{ auth()->user()->name }}" disabled readonly />

        <flux:select :label="__('Language')" id="language" wire:model.live="language" x-data @change="setTimeout(function(){window.location.reload()}, 100)" class="mt-1 block w-full rounded border-gray-300">
            <option value="es">Español</option>
            <option value="en">English</option>
        </flux:select>

    </div>
</div>

Que es consumido mediante una ruta; adicionalmente, como puedes ver, el componente emplea un template llamado web.blade.php que tiene unos menus con textos a traducir.

Lo que hace el componente anterior, es establecer un nuevo idioma en la aplicación, al cambiar el select con el .live, hace que se REDIBUJE SOLO el componente. Si revisas el Network, verás que se hace una sola petición al servidor al cambiar el SELECT. Esto se logra usando wire:model.live, una funcionalidad muy útil si se usa inteligentemente (¡por favor, no lo pongas en todos los campos de texto porque vas a saturar el servidor!).

En este caso, tiene sentido para elementos como selects, donde quiero que se aplique un cambio de idioma en tiempo real.

¿Qué pasa realmente al cambiar el idioma?

Al cambiar el idioma, se ejecuta un método en el servidor (en Laravel, usando App::setLocale()), y le pasamos el nuevo idioma. En este ejemplo uso es para español y en para inglés. Ya tengo todas las traducciones configuradas desde el inicio del proyecto, así que se aplican inmediatamente.

El punto clave aquí es que SOLO recarga el componente, no toda la página. Esto lo puedes notar fácilmente porque al cambiar el idioma, el encabezado (parte del layout) no cambia, pero el contenido del componente sí.

¿Por qué es importante este comportamiento?

Esto es precisamente lo que pasa en una SPA: solo se actualiza la parte que realmente se necesita. Si yo quisiera que toda la página se recargara, podría emitir un evento al componente padre (o incluso al layout), pero en este caso no lo hice porque no era necesario. Tampoco creo que el usuario esté cambiando de idioma cada cinco segundos.

Cómo crear un componente Livewire (paso a paso)

La creación de un componente es directa. Laravel ofrece el comando:

$ php artisan make:livewire User/Profile

Esto genera:

app/Livewire/User/Profile.php (la clase del componente)

resources/views/livewire/user/profile.blade.php (la vista)

Crear el componente con make:livewire

También puedes crear componentes inline:

$ php artisan make:livewire user-profile --inline

Así tienes clase y vista en un mismo archivo, útil para prototipos rápidos.

Estructura básica: clase PHP y vista Blade

Los componentes suelen contener:

  • Propiedades públicas → forman parte del estado
  • Método render() → define qué vista se usa
  • Método mount() → inicialización del componente
  • Métodos de acción → llamados desde la vista
  • Omitir render() y usar convenciones cuando conviene

Si usas la estructura por defecto, Livewire sabe cuál vista corresponde a cada clase y puedes omitir render() totalmente.

Personalmente, en proyectos grandes prefiero mantenerlo visible para controlar el layout o inyectar datos a la vista, pero es opcional.

Propiedades públicas y binding con la vista

Aquí es donde Livewire brilla. Con solo declarar una propiedad pública, ya puedes sincronizarla con la vista:

  • public $language;
  • wire:model, wire:model.live y cuándo usar cada uno
  • wire:model → sincroniza al perder el foco o al enviar el formulario.
  • wire:model.live → sincroniza en tiempo real.

Ciclo de vida de los componentes

Video thumbnail

En Livewire, tenemos múltiples funciones que podemos emplear y que se llaman de manera automática según el estado del componente; esto sucede en muchos otros casos en otras tecnologías con en Android, Flutter, Vue… y estos por nombrar algunos; son sumamente útiles porque con ellos podemos realizar procesos que de otra forma serían complicados de definir; por ejemplo, inicializar propiedades como vamos a realizar luego cuando queramos editar un registro o categoría.

El ciclo de vida del componente. Esto hereda un poco lo que sería un controlador clásico en Laravel. En el esquema tradicional, no existe un ciclo de vida definido; únicamente contamos con el método constructor, que se ejecuta antes de cualquier otra acción cuando resolvemos la petición.

En Livewire, esto es más “vitaminizado”: los componentes tienen un ciclo de vida definido, muy similar a lo que ocurre en Vue, y podemos aprovecharlo para realizar varias acciones.

Para poder entender claramente que es lo que sucede cuando se ejecutan estos métodos del ciclo de vida, activa el log del sistema a nivel de archivo ya que, lo vamos a usar luego:

LOG_CHANNEL=single

Con los logs para los archivos, recuerda que se registran en:

'path' => storage_path('logs/laravel.log'),

En nuestro componente de Save.php de las categorías; vamos a definir los siguientes métodos:

use Log;
***
public function boot()
{
    Log::info("boot");
}

public function booted()
{
    Log::info("booted");
}

public function mount()
{
    Log::info("mount");
}

public function hydrateTitle($value)
{
    Log::info("hydrateTitle $value");
}

public function dehydrateFoo($value)
{
    Log::info("dehydrateFoo $value");
}

public function hydrate()
{
    
    Log::info("hydrate");
}

public function dehydrate()
{
    Log::info("dehydrate");
}

public function updating($name, $value)
{
    Log::info("updating $name $value");
}

public function updated($name, $value)
{
    Log::info("updated $name $value");
}

public function updatingTitle($value)
{
    Log::info("updatingTitle $value");
}

public function updatedTitle($value)
{
        Log::info("updatedTitle $value");
}

Estos métodos, que forman parte del ciclo de vista, lo que hacen es imprimir en el log del sistema un mensaje que corresponde al nombre del método, y si el mismo recibe parámetros, también los registra en el log; estos parámetros son o valores o nombres y valores.

Como pruebas que debes realizar:

  1. Accede a tu log.
  2. Remueve cualquier log que exista en dicho archivo.
  3. Guardas los cambios en dicho archivo.
  4. Accede al componente de Save.php mediante tu navegador.
  5. Revisa el log.
  6. Da un click al botón de submit en el formulario.
  7. Revisa el log.
  8. Escribe algo en alguno de los campos de texto que tenga el wire:model.
  9. Revisa el log.

Métodos principales del ciclo de vida

De todos los métodos disponibles, el que más utilizamos en la práctica es mount.

  • mount: Se ejecuta cuando se monta el componente. Es útil para inicializar datos, por ejemplo, para buscar un post o cargar información que el componente necesita.
  • Existen otros métodos como boot, booted, updating, updated, hydrate, entre otros. Sin embargo, no los utilizamos en la mayoría de los casos, salvo para casos muy específicos de control avanzado o sincronización de datos.

La idea es que vayas haciendo pruebas, eliminando el contenido del log y probando componentes que, actualicen de manera directa las propiedades (como los campos de tipo wire:model), ingresar por primera vez al componente y dar clicks en botones que no actualicen los datos de los wire:model.

storage/logs/laravel.log

[2026-03-23 17:07:15] local.INFO: boot  
[2026-03-23 17:07:15] local.INFO: mount  
[2026-03-23 17:07:15] local.INFO: booted  
[2026-03-23 17:07:15] local.INFO: render  
[2026-03-23 17:07:15] local.INFO: dehydrate  
[2026-03-23 17:07:18] local.INFO: boot  
[2026-03-23 17:07:18] local.INFO: hydrateTitle   
[2026-03-23 17:07:18] local.INFO: hydrate  
[2026-03-23 17:07:18] local.INFO: booted  
[2026-03-23 17:07:18] local.INFO: updating title asas  
[2026-03-23 17:07:18] local.INFO: updatingTitle asas  
[2026-03-23 17:07:18] local.INFO: updated title asas  
[2026-03-23 17:07:18] local.INFO: updatedTitle asas  
[2026-03-23 17:07:18] local.INFO: render  
[2026-03-23 17:07:18] local.INFO: dehydrate  

Por supuesto, podemos aprovechar estas funciones para realizar procesos cruciales para nuestro componente, como por ejemplo, inicializar datos al crear un componente.

Aquí hay tres bloques principales:

  1. Las funciones de tipo update() se invocan cuando se actualizan las propiedades del componente, por ejemplo como el de wire:model; existen variantes como la de updated()updating() cuyo funcionamiento es el mismo salvo que el de updating() se ejecuta antes.
    1. También tenemos la variante de update<Propiedad> la cual la podemos atar a una propiedad.
    2. Al ejecutar la función updated() la propiedad aún no ha sido actualizada, al ejecutar updated(), la propiedad ya fue actualizada.
  2. Funciones de hidrate() son usadas para cuando pasamos datos entre la vista y lo mapea a un objeto en PHP en la clase componente; por ejemplo, cuando se mapea las propiedades a un JSON para pasarlos a la vista para los wire:model:

Data vista a componente

 

  1. Las funciones de dehydrate() son usadas para justamente lo contrario de las de hidrate(), es decir, son usadas para cuando pasamos datos entre la clase componente a la vista; por ejemplo, cuando se mapea las propiedades a un JSON para pasarlos a la vista para los wire:model.

Data componente a vista

 

  1. En definitiva, estas funciones son para las actualizaciones a nivel del componente en el pase de mensaje y no solamente la data del mismo.
  2. Funciones del ciclo de vida básico como la de render()boot(), o booted() que se ejecutan como parte del ciclo de vida al actualizar el componente.
    1. En este renglón también existe la función de mount() la cual se ejecuta solamente una vez al montar el componente, a diferencia de las anteriores y como puedes ver en el log señalado anteriormente.

Resumen de métodos útiles

  • mount: Inicialización de datos. Es el que más usamos.
  • render: Renderiza la vista del componente. Siempre se ejecuta cuando algo cambia.
  • boot y booted: Se ejecutan al iniciar el componente, antes y después de mount. Pueden usarse para tareas de configuración general.
  • updating y updated: Se ejecutan al modificar propiedades vinculadas a la vista (wire:model).
  • hydrate y dehydrate: Se ejecutan al enviar datos entre el backend y el frontend, muy útiles cuando necesitamos manipular datos antes o después de la comunicación con la vista.

En general, en la mayoría de los casos solo necesitamos mount y render. El resto de los métodos son opcionales y se utilizan en situaciones más avanzadas.

Componentes Principales en Livewire

En Livewire, tenemos muchos componentes que podemos implementar y reutilizar, para mi, es de lo mejor de Laravel Livewire veamos algunos.

Componente de mostrar mensaje de acción realizada en Laravel Livewire

Si estás empleando Livewire, te pudieras pregunta cómo podemos mostrar un mensaje por unos pocos segundos para que luego ocultarlo; esto es particularmente útil para cuando hacemos una operación de inserción, actualización, eliminación, el usuario se autenticó, etc; estos mensajes se conocer como mensajes de acción realizada, no son mensajes de confirmación en Livewire como vimos antes ya que, el usuario no podrá cancelar la acción realizada, solamente aparecerá un mensaje para que el usuario sepa que acción se acaba de realizar.

En Livewire se puede implementar este tipo de componentes muy fácilmente; pero para lograr algo de inspiración, tenemos un componente que es interesante de ver, y es:

https://github.com/laravel/jetstream/blob/2.x/resources/views/components/action-message.blade.php

El cual con ayuda de Alpine.js, permite determinar el tiempo de vida para visualizar este trozo de HTML:

<div x-data="{ shown: false, timeout: null }"
    x-init="@this.on('{{ $on }}', () => { clearTimeout(timeout); shown = true; timeout = setTimeout(() => { shown = false }, 2000);  })"
    x-show.transition.out.opacity.duration.1500ms="shown"
    x-transition:leave.opacity.duration.1500ms
    style="display: none;"
    {{ $attributes->merge(['class' => 'text-sm text-gray-600']) }}>
    {{ $slot->isEmpty() ? 'Saved.' : $slot }}
</div>

En la práctica podemos crear uno basado en el anterior como:

@if (session('status'))
    <div x-data="{ shown: false }"
        x-init="clearTimeout(2000); shown = true; timeout = setTimeout(() => { shown = false }, 2000);"
        x-show.transition.out.opacity.duration.1500ms="shown" x-transition:leave.opacity.duration.1500ms>
        <div class="p-1 mt-1 bg-purple-500 rounded-md">
            <x-card class="m-0">
                <h3 class="text-xl">
                    {{ session('status') }}
                </h3>
            </x-card>
        </div>
    </div>
@endif

Lo importante es emplear Alpine.js para ocultar el contenido mediante la función de setTimeout pasado cierto tiempo, por lo demás, ya conocemos como mostrar un componente de acción realizada que se muestra por pocos segundos.

Interfaces Fluidas con wire:text y Actualizaciones Optimistas

Video thumbnail

Al revisar la documentación oficial de Livewire, una de las primeras equivalencias que resalta es el uso de la directiva wire:text como alternativa a las clásicas llaves dobles de Blade ({{ $propiedad }}). Si bien ambas herramientas cumplen con el propósito fundamental de renderizar y mostrar un valor por pantalla en el navegador, la diferencia radica en la forma en que gestionan la latencia y en su capacidad para implementar actualizaciones optimistas (Optimistic Updates).

El Problema de la Reactividad Basada en Servidor

En la arquitectura estándar de Livewire, la reactividad depende de un ciclo de peticiones HTTP en segundo plano. Evaluemos un escenario común: un botón para incrementar un contador de interacciones o "me gusta" (likes).

<button wire:click="incrementarLike">
    ❤️ {{ $likes }}
</button>

Bajo este enfoque tradicional, el flujo se procesa de la siguiente manera:

  1. El usuario hace clic en el botón.
  2. Livewire intercepta el evento y dispara una petición asíncrona hacia el servidor.
  3. El servidor procesa la lógica en PHP, incrementa la propiedad en el backend y re-renderiza el componente.
  4. El navegador recibe la respuesta HTTP con el nuevo estado y actualiza el DOM (por ejemplo, pasando de 5 a 6).

Este ciclo introduce un desfase temporal (delay) inevitable. Si el usuario cuenta con una conexión a internet lenta, experimenta problemas de latencia o el servidor se encuentra bajo una alta carga de peticiones, la interfaz se percibirá pesada y poco responsiva, ya que el valor en pantalla no cambiará hasta que la petición del servidor finalice con éxito.

Arquitectura de una Actualización Optimista

Para resolver este problema de fluidez, se emplean las actualizaciones optimistas, un concepto arquitectónico utilizado ampliamente en frameworks de frontend (como las peticiones configuradas en Inertia.js o aplicaciones SPA tradicionales).

Una actualización optimista asume de manera anticipada que el servidor procesará la instrucción de forma correcta. En lugar de bloquear la interfaz de usuario esperando la respuesta de la red, el sistema modifica el valor visual localmente en el cliente de manera instantánea.

En Livewire, este comportamiento se logra combinando wire:text con el objeto global wire en JavaScript para manipular el estado del lado del cliente.

resources\views\pages\demo\⚡wire-text.blade.php

<?php

use Livewire\Component;
use App\Models\Post;

new class extends Component {
    public $likes;

    public function mount()
    {
        $this->likes = 5;
    }

    public function like()
    {
        $this->likes++;
    }
};
?>

<div>
    <flux:button x-on:click="$wire.likes++" wire:click="like">❤️ Like</flux:button>

    <ul>
        <li>
            Likes wire-text: <span wire:text="likes"></span>
        </li>
        <li>
            Likes Blade: {{ $likes }}
        </li>
    </ul>
</div>

Al inspeccionar este fragmento, se observa una dualidad operativa:

  • En el Servidor (wire:click): Se invoca el método de PHP para persistir el cambio en la base de datos de manera asíncrona.
  • En el Cliente (@click="$wire.likes++"): Se utiliza JavaScript para incrementar inmediatamente el valor de la propiedad en el navegador.

Gracias a que wire:text escucha los cambios del estado local de forma inmediata, la interfaz se actualiza instantáneamente ante los ojos del usuario sin importar la velocidad de la conexión de red.

Simulación en Escenarios de Desconexión

La ventaja técnica de wire:text se evidencia con claridad al realizar pruebas de rendimiento simulando un entorno sin conectividad (Offline) en las herramientas de desarrollo del navegador:

  • Comportamiento de Blade ({{ }}): Al interrumpir la conexión a internet y hacer clic en el botón, la interfaz se congela. Debido a que la petición de red falla y no hay respuesta del servidor, el DOM nunca se entera del cambio y el valor permanece estático.
  • Comportamiento de wire:text: Bajo las mismas condiciones de desconexión, el valor en pantalla se incrementa instantáneamente. Aunque la consola del navegador registre errores debido al fallo de la petición HTTP, el componente visual refleja el cambio en el cliente porque no depende de la resolución del tráfico de red para actualizar la vista. La directiva asume con optimismo que los datos se sincronizarán en cuanto el canal de comunicación se restablezca.

Consideraciones de Rendimiento y Buenas Prácticas

A pesar de los beneficios visuales que ofrece wire:text, su implementación debe responder a un criterio de diseño de software consciente del consumo de recursos:

  • Evitar la saturación del servidor: Disparar peticiones HTTP masivas al servidor ante acciones repetitivas del usuario (como clics consecutivos en un botón) es una práctica ineficiente que puede comprometer la concurrencia en entornos de producción reales durante horas pico.
  • Agrupación de operaciones: Para flujos de trabajo densos, como la edición de formularios complejos, la mejor estrategia arquitectónica consiste en mantener las mutaciones de datos controladas localmente en el cliente mediante JavaScript o Alpine.js. Las peticiones directas al backend con Livewire deben reservarse de forma estricta para la acción final de envío y persistencia de datos.

El uso estratégico de wire:text permite diseñar aplicaciones híbridas de alto rendimiento, logrando que el sistema se perciba tan rápido como una aplicación nativa de frontend sin perder las ventajas de centralizar la lógica de negocio en el backend con PHP.

Preguntas frecuentes sobre los componentes de Livewire

  • ¿Livewire recarga toda la página?
    • No, solo el componente afectado gracias al DOM morphing.
  • ¿Cuándo usar wire:model.live?
    • Cuando la interacción requiere inmediatez: selects, sliders, búsquedas rápidas.
  • ¿Qué diferencia hay entre un componente Blade y uno Livewire?
    • Blade es estático. Livewire es reactivo.
  • ¿Livewire sigue siendo útil si sé JavaScript?
    • Sí. Añade fluidez a Laravel sin necesidad de levantar un ecosistema JS completo.

Conclusión

Esto nos da una idea clara de cómo Livewire organiza la estructura de sus componentes y nos permite inicializar y controlar la data de forma eficiente.

No todos los métodos se usan en un desarrollo típico, pero es importante conocerlos y entender su propósito. Más adelante, si necesitamos alguna funcionalidad avanzada, podemos explorarlos más a fondo.

Con esto, damos por terminada esta clase. Todo el código estará disponible comentado en la sección correspondiente para que puedas experimentar y profundizar si lo deseas.

En conclusión, las funciones más usadas para el ciclo de vida de los componentes son las de update, en cualquiera de sus variantes y la de mount() para inicializar datos.

Los componentes Livewire combinan simplicidad con potencia. Te permiten crear interfaces reactivas sin frameworks externos, manteniendo todo en PHP. Y si aprovechas bien las propiedades, acciones, layouts y flujos internos, puedes construir desde paneles administrativos hasta sistemas completos en tiempo real.

En mi experiencia, entender cómo Livewire redibuja solo lo necesario fue el punto de inflexión para sacarle verdadero partido. Y detalles como no abusar de .live o usar wire:key correctamente marcan una diferencia enorme en rendimiento.

El siguiente paso, es, aprender a manejar los formularios en Laravel Livewire.

Muestro un ejemplo de como funciona los componentes de Livewire, que es lo que recarga al momento de hacer el re render del mismo.


Únete a la comunidad de desarrolladores que han decidido dejar de picar código y empezar a construir productos reales. Recibe mis mejores trucos de arquitectura cada semana:

Acepto recibir anuncios de interes sobre este Blog.