Curso de Laravel

Componentes Blade en Laravel: vistas reutilizables

Por Víctor Peña · Publicado el

Hola, ¿cómo están? Continuando con el curso de Laravel, hoy veremos los componentes de Blade, que son la evolución natural de los @include que vimos en la lección anterior.

¡Empecemos!

El problema con los includes

Un @include funciona, pero tiene dos limitaciones que se notan enseguida.

Hereda todas las variables del contexto. No está claro qué necesita realmente:

@include('partials.alerta')

¿Qué variables espera ese archivo? Hay que abrirlo para saberlo. Y si olvidas definir una, el error aparece dentro del include.

No admite contenido variable. Si quieres una tarjeta cuyo interior cambie en cada uso, un include no te sirve.

Los componentes resuelven las dos cosas.

Crear un componente

php artisan make:component Alerta

Eso genera dos archivos:

  • app/View/Components/Alerta.php — la clase
  • resources/views/components/alerta.blade.php — la plantilla

La clase:

<?php

namespace App\View\Components;

use Illuminate\View\Component;
use Illuminate\View\View;

class Alerta extends Component
{
    public function __construct(
        public string $tipo = 'info',
        public ?string $titulo = null,
    ) {}

    public function render(): View
    {
        return view('components.alerta');
    }
}

La plantilla:

<div class="alerta alerta-{{ $tipo }}">
    @if ($titulo)
        <strong>{{ $titulo }}</strong>
    @endif

    {{ $slot }}
</div>

Y así se usa:

<x-alerta tipo="exito" titulo="Listo">
    El paciente se registró correctamente.
</x-alerta>

Fíjate en tres cosas: el prefijo x-, que los parámetros del constructor se pasan como atributos HTML, y que el contenido entre las etiquetas llega en $slot.

Esa última es la diferencia con los includes: el componente define la estructura y quien lo usa decide el contenido.

Pasar datos

Los valores fijos se pasan como atributos normales:

<x-alerta tipo="error" />

Y las variables con dos puntos delante:

<x-alerta :tipo="$estado" :titulo="$paciente->nombre" />

Ese : significa «esto es una expresión PHP, no un texto». Es el error más frecuente al empezar:

<x-tarjeta paciente="$paciente" />    {{-- pasa el texto "$paciente" --}}
<x-tarjeta :paciente="$paciente" />   {{-- pasa el objeto --}}

Componentes anónimos

Cuando el componente no necesita lógica, no hace falta la clase:

php artisan make:component boton --view

Solo se crea resources/views/components/boton.blade.php:

@props([
    'tipo' => 'primario',
    'href' => null,
])

@if ($href)
    <a href="{{ $href }}" {{ $attributes->merge(['class' => "boton boton-$tipo"]) }}>
        {{ $slot }}
    </a>
@else
    <button {{ $attributes->merge(['class' => "boton boton-$tipo"]) }}>
        {{ $slot }}
    </button>
@endif

La directiva @props declara qué recibe el componente y con qué valores por defecto.

Los componentes anónimos son los que más vas a escribir. La mayoría de piezas de interfaz —botones, tarjetas, campos de formulario— no necesitan lógica.

El objeto $attributes

Aquí está una de las funcionalidades más útiles y menos conocidas.

Los atributos que no declaraste en @props se recogen en $attributes, y puedes reenviarlos al HTML:

<x-boton tipo="peligro" id="eliminar" data-confirmar="true">
    Eliminar
</x-boton>

En la plantilla, {{ $attributes }} imprime id="eliminar" data-confirmar="true".

Y merge() combina tus clases con las que pase quien lo use:

{{ $attributes->merge(['class' => 'boton']) }}
<x-boton class="ancho-completo">Guardar</x-boton>
{{-- resultado: class="boton ancho-completo" --}}

Otros métodos útiles:

{{ $attributes->class(['boton', 'boton-activo' => $activo]) }}
{{ $attributes->except(['tipo']) }}
{{ $attributes->only(['id', 'name']) }}

Slots con nombre

Cuando el componente tiene varias zonas de contenido:

{{-- components/tarjeta.blade.php --}}
<div class="tarjeta">
    <div class="tarjeta-cabecera">
        {{ $cabecera }}
    </div>

    <div class="tarjeta-cuerpo">
        {{ $slot }}
    </div>

    @isset($pie)
        <div class="tarjeta-pie">
            {{ $pie }}
        </div>
    @endisset
</div>
<x-tarjeta>
    <x-slot:cabecera>
        <h2>{{ $paciente->nombre }}</h2>
    </x-slot:cabecera>

    <p>Cédula: {{ $paciente->cedula }}</p>
    <p>Teléfono: {{ $paciente->telefono }}</p>

    <x-slot:pie>
        <a href="{{ route('pacientes.edit', $paciente) }}">Editar</a>
    </x-slot:pie>
</x-tarjeta>

El @isset($pie) hace que el pie solo aparezca si se lo pasaron.

El caso que más rentabiliza: campos de formulario

Este es el ejemplo donde los componentes ahorran más código real.

{{-- components/campo.blade.php --}}
@props([
    'nombre',
    'etiqueta',
    'tipo' => 'text',
    'valor' => null,
    'requerido' => false,
])

<div class="campo">
    <label for="{{ $nombre }}">
        {{ $etiqueta }}
        @if ($requerido)<span class="obligatorio">*</span>@endif
    </label>

    <input
        type="{{ $tipo }}"
        id="{{ $nombre }}"
        name="{{ $nombre }}"
        value="{{ old($nombre, $valor) }}"
        @if ($requerido) required @endif
        {{ $attributes->class(['campo-entrada', 'campo-error' => $errors->has($nombre)]) }}
    >

    @error($nombre)
        <span class="mensaje-error">{{ $message }}</span>
    @enderror
</div>

Y el formulario completo queda así:

<form method="POST" action="{{ route('pacientes.store') }}">
    @csrf

    <x-campo nombre="cedula" etiqueta="Cédula" requerido />
    <x-campo nombre="nombre" etiqueta="Nombre" requerido />
    <x-campo nombre="apellido" etiqueta="Apellido" requerido />
    <x-campo nombre="fecha_nacimiento" etiqueta="Fecha de nacimiento" tipo="date" requerido />
    <x-campo nombre="correo" etiqueta="Correo" tipo="email" />

    <x-boton type="submit">Guardar</x-boton>
</form>

Compáralo con escribir a mano la etiqueta, el input, el old() y el @error cinco veces. Y si mañana cambias el diseño de los campos, lo cambias en un archivo.

Componentes en subcarpetas

resources/views/components/
├── formulario/
│   ├── campo.blade.php
│   └── select.blade.php
└── ui/
    ├── boton.blade.php
    └── tarjeta.blade.php

Se usan con punto:

<x-formulario.campo nombre="cedula" etiqueta="Cédula" />
<x-ui.boton>Guardar</x-ui.boton>

En proyectos que crecen, esa organización se agradece.

El layout como componente

Es la alternativa moderna al @extends que vimos antes:

{{-- components/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="es">
<head>
    <title>{{ $titulo ?? 'Sistema Clínica' }}</title>
    @vite(['resources/css/app.css'])
</head>
<body>
    <header>...</header>
    <main>{{ $slot }}</main>
</body>
</html>
<x-layouts.app titulo="Pacientes">
    <h1>Listado de pacientes</h1>
    ...
</x-layouts.app>

Los dos enfoques son válidos. @extends es el clásico y lo verás en la mayoría de proyectos existentes; el componente es más consistente con el resto de Blade. Elige uno y sé coherente en todo el proyecto.

Cuándo componente y cuándo include

Situación Usa
El contenido interior cambia Componente
Recibe parámetros claros Componente
Se usa en muchos sitios Componente
Un fragmento fijo, usado en dos vistas @include
Un formulario compartido entre crear y editar @include

En general, componente por defecto. El include queda para fragmentos que dependen del contexto y no valen la pena parametrizar.

Errores comunes

  • Olvidar los dos puntos al pasar una variable, y enviar el texto literal.
  • No declarar @props, y que el componente reciba variables por accidente.
  • No usar $attributes->merge(), obligando a modificar el componente cada vez que hace falta una clase.
  • Componentes que hacen consultas. Los datos vienen del controlador.
  • Mezclar @extends y layout-componente en el mismo proyecto.

Para cerrar

Los componentes son a las vistas lo que las funciones son al código: una forma de escribir una vez y usar muchas.

El consejo práctico: empieza por un componente de campo de formulario. Es donde más repetición hay en cualquier proyecto, y en cuanto lo tengas no vas a querer volver atrás.

En la siguiente lección veremos la validación, que encaja directamente con esos campos.

Saludos y éxitos.

Norvic Software

Desarrollamos el software que tu empresa necesita

Somos una fábrica de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.

Solicitar cotizaciónVer todos los servicios

Cotización sin costo · Respuesta directa por WhatsApp