Los componentes son similares a los layouts y los fragmentos de vista; pero con esteroides, ya que, los mismos permiten la definición de pase de parámetros (como los fragmentos de vista), definición de slot, para colocar contenido clave, pase de atributos, llamados a métodos, entre otras opciones.
¿Qué es un componente en Blade?
Un componente es, esencialmente, una pieza reutilizable. El objetivo principal es evitar las aplicaciones monolíticas y crear estructuras divididas en bloques sencillos.
Ejemplo práctico en un Blog:
Imagina un listado de publicaciones. Podemos dividirlo así:
- Componente Item: La tarjeta que muestra una publicación individual.
- Componente Acciones: Dentro del item, un bloque para "favoritos" o "me gusta".
- Componente Comentarios: Un bloque que a su vez contiene otros componentes para cada comentario individual.
Estructura y Ventajas
La idea central de los componentes, es que, con las mismas podemos hacer verdaderas micro aplicaciones, queriendo decir con micro aplicaciones, pequeños "componentes" que realizan una función en particular, cómo listar un post; pero, desde este listado, el mismo componente es capaz de brindar opciones sobre ese mismo componente, como por ejemplo, asignarlo a un favorito, remover un favorito, agregarlo a una clasificación, indicar si quieres mostrarlo en base a una condición y tareas de este tipo; la ventaja fundamental con respecto a los fragmentos de vista, es que, toda esta lógica de opciones está dentro del componente, y no depende de componentes padres (como sucedería con los fragmentos de vista).
Tipos de Componentes en Laravel
Hay dos enfoques para escribir componentes:
- Componentes basados en clases, que constan de una vista y una clase.
- Componentes anónimos, que constan solamente de una vista.
Para crear un componente, lo podemos hacer mediante artisan con:
$ php artisan make:component <TuComponente>Al cual puedes indicar la jerarquía de carpetas, y la opción de --view para indicar que quieres un componente con solamente una vista.
O si vas a colocar en carpetas y es un componente con clases tienes que colocar si o si, la primera letra en mayúscula (UpperCamelCase) y separar por /:
$ php artisan make:component Forms/InputEsta lección es mayormente práctica, ya que, la idea es introducir algunas funcionalidades que tienen este tipo de elementos en base a ejemplos.
Estructura inicial
En este apartado, nos interesa construir parte del módulo web de cara al usuario final, es decir, en donde vamos a tener nuestros listados de posts para que sean consumidos por un internauta.
Vamos a emplear los componentes en conjunto con un controlador, así que, vamos a crear ese controlador con:
$ php artisan make:controller blog/BlogController -m PostY va a tener la siguiente estructura:
app\Http\Controllers\blog\BlogController.php
<?php
namespace App\Http\Controllers\blog;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\View\View;
class BlogController extends Controller
{
function index()
{
$posts = Post::paginate(2);
return view('blog.index', compact('posts'));
}
function show(Post $post){
return view('blog.show', ['post' => $post]);
}
}Dejando solamente el método de index(), para el listado y show() para el detalle.
Sus rutas serán como:
routes\web.php
use App\Http\Controllers\web\BlogController;
***
Route::group(['prefix' => 'blog'], function () {
Route::controller(BlogController::class)->group(function () {
Route::get('', [BlogController::class, 'index'])->name('blog.index');
Route::get('detail/{post}', [BlogController::class, 'show'])->name('blog.show');
});
});Las rutas están compuestas de un agrupamiento para el prefijo, uno para el controlador y, finalmente las rutas.
Componentes anónimos: Vista de listado
Los componentes anónimos son aquellos que solamente constan de una vista; para crear un componente de este tipo:
$ php artisan make:component blog.post.index --viewEsto nos creará el archivo en resources/views/components/blog/post/index.blade.php. Organizarlo así (con puntos) nos permite tener carpetas separadas por si luego queremos añadir componentes para categorías o comentarios.
Y con esto, se creará un componente de vista en:
resources/views/components/blog/post/index.blade.php
Estructura del Componente (Blade)
Al cual, agregaremos el siguiente contenido:
resources\views\components\blog\post\index.blade.php
<div>
@foreach ($posts as $p)
<div class="card card-white mt-2">
<h3>{{ $p->title }}</h3>
<a href="{{ route('blog.show', $p) }}">Go</a>
<p>{{ $p->description }}</p>
</div>
@endforeach
</div>Para usarla desde:
resources\views\blog\index.blade.php
Con el siguiente contenido:
resources\views\blog\index.blade.php
@extends('blog.master')
@section('content')
<x-blog.post.index :posts="$posts"/>
@endsectionLayout
El layout luce como:
resources\views\blog\master.blade.php
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Blog</title>
@vite(['resources/css/blog.css'])
</head>
<body>
<div class="min-h-screen bg-gray-100 dark:bg-gray-900">
<main>
<div class="container mx-auto">
@yield('content')
</div>
</main>
</div>
</body>
</html>Modularización del Estilo con CSS
Lo bonito de trabajar de forma organizada es que podemos separar los estilos. No queremos cargar todo el CSS del Dashboard en el Blog.
Para esto, he creado un archivo específico: blog.css. Al dividir los archivos (dashboard, blog, base), evitamos navegar en un mar de código innecesario.
- Duplicamos el archivo base.
- Comentamos o eliminamos lo que no usaremos (como tablas complejas o formularios de administración).
- Mantenemos lo esencial: contenedores y tarjetas.
Esta modularidad es la que nos permite cambiar el aspecto de una página completa en cuestión de segundos, simplemente alternando el archivo de estilo que importamos.
resources\css\blog.css
@import 'tailwindcss';
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
@source '../../storage/framework/views/*.php';
@source '../**/*.blade.php';
@source '../**/*.js';
@theme {
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol', 'Noto Color Emoji';
}
@import './components/button.css';
/* @import './components/form.css'; */
@import './components/container.css';
@import './components/card.css';
/* @import './components/boxs.css'; */vite.config.js
input: ['resources/css/app.css','resources/css/dashboard.css','resources/css/blog.css', 'resources/js/app.js'],Listado en el componente
Como puedes ver, consumimos el contenido con una estructura similar al que hacemos con el método de view(), y para pasar parámetros, lo hacemos a la “manera Vue” con dos puntos ":" seguido del nombre de la variable:
resources\views\components\blog\post\index.blade.php
<div>
@foreach ($posts as $p)
<div class="card mt-2">
<h3>
{{ $p->title }}
</h3>
<a href="{{ route('blog.show', $p) }}">Go</a>
<p>{{ $p->description }}</p>
</div>
@endforeach
{{ $posts->links() }}
</div>Veremos:

Es importante que la estructura que mostramos en este apartado, en donde el controlador emplea un componente anónimo para renderizar el contenido es netamente demostrativo, es decir, no es una estructura que debas de seguir exactamente igual a lo mostrado, aquí también hubieras podido emplear solamente el controlador con su vista asociada que devuelva el listado de publicaciones, tal cual hicimos antes en el dashboard o emplear un componente con clase (como veremos más adelante).
Slot
Los slots permiten adicionar contenido extra HTML/PHP a nuestro componente; con los slots, podemos personalizar fácilmente los componentes indicando mediante secciones aquellos apartados a los cuales queramos colocar dicho contenido extra.
Por ejemplo, en un componente de listado que luce como el siguiente:

Puede interesarnos personalizar el título, footer y tal vez un apartado para agregar contenido adicional (como enmarcamos en la imagen anterior); cada uno de estos apartados, pueden ser un slot.
- Podemos usar un listado como el anterior para indicar los últimos posts.
- También, para construir un listado en base a una categoría establecida, en este caso, cambiamos el título y el footer para mostrar información sobre la categoría y en el contenido extra colocamos los cursos relacionados con las categorías.
Claro está, estos son solamente dos ejemplos, lo importante es notar que podemos crear múltiples listados usando el mismo componente y actualizar las secciones para personalizar la experiencia en cada uno de los slots.
Apartados como el anterior, se pueden construir fácilmente usando los componentes y los slots.
Los slots pueden ser empleados de diversas maneras, veamos cuales son.
Slot por defecto
Por defecto, podemos usar el componente como si fuera un elemento HTML, indicando una etiqueta de apertura y otra de cerrado; en el medio (entre las etiquetas), colocamos el contenido del slot por defecto:
resources\views\blog\index.blade.php
@extends('blog.master')
@section('content')
<x-blog.post.index :posts="$posts">
Posts
</x-blog.post.index >
@endsectionLuego, desde el componente, consumimos este contenido mediante una variable llamada slot:
resources\views\components\blog\post\index.blade.php
<div>
<h1>{{ $slot }}</h1>
@foreach ($posts as $p)
<div class="card mt-2">
<h3>
{{ $p->title }}
</h3>
<a href="{{ route('blog.show', $p) }}">Ir</a>
<p>{{ $p->description }}</p>
</div>
@endforeach
{{ $posts->links() }}
</div>Veremos el título de "Posts" en un H1 :

Slots con nombre (Named Slots)
Como se mencionó antes, muchas veces es necesario definir múltiples apartados en un componente para definir el contenido extra, por lo tanto, con los componentes podemos tener estructuras complejas pasando múltiples datos para personalizar los mismos; para ello, podemos definir slots con nombres; el nombre es usado tanto para declarar el slot como su contenido.
Podemos definir un slot específico, como un header, y lo ideal es envolverlo en un condicional @isset para que no rompa la aplicación si decidimos no enviarlo:
En este ejemplo, definimos tres slots con nombre, "header", "footer" y "extra" respectivamente:
resources\views\blog\index.blade.php
@extends('blog.master')
@section('content')
<x-blog.post.index :posts='$posts'>
Post List
@slot('footer')
Footer
@endslot
@slot('extra')
Extra
@endslot
</x-blog.post.index>
@endsectionCómo enviarlo desde la vista
A nivel de la vista, se usan los nombre de los slots como variables:
resources\views\components\blog\post\index.blade.php
<div>
<br>
<h1>{{ $slot }}</h1>
@if (isset($header))
<h1>{{ $header }}</h1>
@endif
@foreach ($posts as $p)
<div class="card card-white mt-2">
<h3>{{ $p->title }}</h3>
<a href="{{ route('blog.show', $p) }}">Ir</a>
<p>{{ $p->description }}</p>
</div>
@endforeach
<br>
@isset($extra)
<h1>{{ $extra }}</h1>
@endisset
<h1>{{ $footer }}</h1>
{{ $posts->links() }}
</div>Y veremos:

Listado de publicaciones, slot por defecto y slot con nombre
En el ejemplo anterior, puedes ver que desde el componente, se verifica si se está suministrando el slot con nombre mediante condicionales y la directiva isset(), por lo tanto, dependiendo de cómo definas tus slots, pueden que sean opcionales, como en el caso del slot para el título y extra u obligatorios como en el caso del slot del footer.
Slots con nombre en una línea
Muchas veces no es necesario pasar un contenido HTML completo en los slots, si no, solamente un valor, un texto o un número; en estos casos, podemos usar la siguiente sintaxis:
resources\views\blog\blog\index.blade.php
<x-blog.post.index :posts="$posts" title='Listado inicial'>
@slot('other', 'Extra')
</x-blog.post.index>Y desde el componente:
resources\views\components\web\blog\post\index.blade.php
No hay nada que cambiar y veremos la misma imagen mostrada anteriormente.
¿Slots o Props? (Diferencias clave)
Seguramente te preguntarás: ¿Por qué usar un Slot si puedo pasar un String por un Prop?
La diferencia principal es el HTML.
- Props: Son ideales para pasar datos puros (strings, booleanos, colecciones o modelos).
- Slots: Son perfectos cuando quieres pasar bloques de código HTML. Por ejemplo, si en un lugar quieres que el título sea un <h1> y en otro un <h2> con un ícono al lado, el Slot te da esa libertad total de renderizado.
Uso de los props en los componentes de Laravel para establecer clases por defecto
Muchas veces necesitamos configurar un componente indicando una clase por defecto pero que la misma pueda ser personalizada al consumir el componente de Laravel, por ejemplo, un componente de carta, en el cual, por defecto usemos un color blanco para el fondo pero, al momento de seleccionar otro color de fondo, el de blanco no se emplee.
Y esto es precisamente un punto muy importante, evitar que se utilice el color de fondo en blanco y no el especificado al momento de consumir el componente, especificamente estamos hablando de clases de tailwind, ya que, cargar ambos al mismo tiempo traerá el problema de que uno de ellos sobrescribirá al otro dependiendo en que parte de la hoja de estilo se este empleando; para evitar esto, podemos emplear los props a nivel de los componentes, dando un valor por defecto:
@props(['id' => null, 'bg' => 'bg-white'])Y desde el componente, lo inyectamos de la siguiente manera:
@props(['id' => null, 'bg' => 'bg-white'])
@isset($logo)
<div>
{{ $logo }}
</div>
@endisset
<div {!! $attributes->merge(['class' => 'w-full mt-2 '. $bg .' shadow-md overflow-hidden']) !!}>
@isset($extra)
<div class="border-b border-gray-200">
{{ $extra }}
</div>
@endisset
@isset($title)
<div class="border-b border-gray-200">
<div class="px-3 pb-1 pt-2">
{{ $title }}
</div>
</div>
@endisset
<div class="px-3 py-2">
{{ $slot }}
</div>
</div>
Luego, al momento de emplear el componente anterior, que se llama card.blade.php, si queremos utilizar el color que trae por defecto la carta, que sería el color de blanco:
<x-card class="mb-10">
content
</x-card>O si queremos seleccionar otro color de fondo, por ejemplo, un morado:
<x-card class="mb-10" bg="bg-purple-300">
content
</x-card>Y de esta forma tan facil, podemos utilizar estos mismos pasos a cualquier otra clase que no necesariamente tiene que ser de color de fondo con los componentes en Laravel.
x