Índice de contenido
- Impresión de HTML
- Estructuras de Control y Condicionales
- Condicionales @if y @switch
- El bucle @foreach y la variable $loop
- El potente @forelse
- Manejo de errores y formularios
- Variables de entorno
- Integración con frontend (@verbatim)
- Uso de PHP dentro de Blade
- Directivas Avanzadas: Scripts y Stacks
- 1. La directiva @once
- 2. @push y @stack
- Clases Dinámicas y Atributos
- Seguridad y Escape de Datos HTML
- Imprimir HTML en blade
- La Directiva @include y sus Variantes
- Controlando la Existencia y Condiciones
- @includeIsolated: Blindando tu Vista
- Renderizando Colecciones con @each
Descubre cómo potenciar el desarrollo de tus interfaces con Laravel Blade, el motor de plantillas más potente y elegante del ecosistema PHP.
En esta guía práctica, exploraremos desde las directivas básicas de control como @if y @foreach, hasta el uso avanzado de Componentes, Stacks y Slots, herramientas esenciales para crear vistas dinámicas, reutilizables y altamente eficientes en tus proyectos web modernos.
Aprenderás a optimizar el rendimiento de tu frontend mediante técnicas profesionales como la carga única de scripts con @once y el manejo inteligente de pilas con @push. Ya sea que estés dando tus primeros pasos o busques perfeccionar tu flujo de trabajo con Inertia o Livewire, dominar las directivas personalizadas y la lógica de visualización de Blade te permitirá escribir un código mucho más limpio, seguro y fácil de mantener.
Blade no es solo sobre componentes; de hecho, esa parte está más arriba en la documentación. Aproximadamente un tercio de la página cubre funcionalidades clave, lo que demuestra lo importante que es Blade dentro de Laravel.
Aunque no sea de mis tecnologías favoritas dentro de Laravel (especialmente en comparación con Livewire o Inertia), sigue siendo fundamental entenderlo.
Este es el controlador que emplearemos para la practica:
class CourseController extends Controller
{
public function index(Request $request)
{
return view('pruebas.dashboard', [
'user' => 'Andrés Cruz',
'role' => 'admin',
'status' => 2, // 1: Pendiente, 2: Activo, 3: Suspendido
'courses' => [
['id' => 1, 'name' => 'Laravel 13', 'type' => 'Backend', 'premium' => true],
['id' => 2, 'name' => 'Vue.js 3', 'type' => 'Frontend', 'premium' => false],
['id' => 3, 'name' => 'AWS Cloud', 'type' => 'DevOps', 'premium' => true],
],
'tags' => [], // Array vacío para probar @forelse
'isSubscribed' => true,
]);
}
}Plantilla maestra:
html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>@yield('title', 'Plataforma Educativa')title>
<script src="https://cdn.tailwindcss.com">script>
@stack('styles')
head>
<body class="bg-slate-50 p-10">
<header class="mb-5 p-4 bg-white shadow">
{{-- Directiva de Autenticación --}}
@auth
<p>Conectado como: <strong>@yield('user_name')strong>p>
@endauth
header>
<main class="container mx-auto">
@yield('content')
main>
{{-- Directiva para inyectar scripts desde vistas hijas --}}
@stack('scripts')
body>
html>Lo característico es el @stack('scripts') que explicamos más adelante.
La vista que exploraremos a lo largo del apartado:
@extends('pruebas.layout')
@section('title', 'Panel de Control - Blade 13')
@section('user_name', $user)
@section('content')
<h1 class="text-2xl font-bold mb-6">Explorando Directivas de Bladeh1>
{{-- 1. Condicionales If / Else --}}
<section class="mb-8">
<h2 class="text-lg font-semibold">Estado de la cuenta:h2>
@if($status === 1)
<span class="text-yellow-600">Esperando verificación...span>
@elseif($status === 2)
<span class="text-green-600">Cuenta verificada correctamente.span>
@else
<span class="text-red-600">Cuenta restringida.span>
@endif
section>
{{-- 2. Directiva Switch --}}
<section class="mb-8">
@switch($role)
@case('admin')
<div class="bg-blue-100 p-2 text-blue-800">Acceso total de administradordiv>
@break
@case('editor')
<div class="bg-green-100 p-2 text-green-800">Acceso de edicióndiv>
@break
@default
<div class="bg-gray-100 p-2">Acceso de lecturadiv>
@endswitch
section>
{{-- 3. Bucles y la variable $loop --}}
<section class="mb-8">
<h2 class="text-xl font-bold mb-3">Listado de Cursosh2>
<ul class="space-y-2">
@foreach($courses as $course)
<li @class([
'p-3 rounded border',
'bg-yellow-50 border-yellow-200' => $course['premium'],
'bg-white border-gray-200' => !$course['premium']
])>
<strong>#{{ $loop->iteration }}strong> - {{ $course['name'] }}
<span class="text-sm italic">({{ $course['type'] }})span>
@if($loop->first) <b class="text-blue-500">[NUEVO]b> @endif
@if($loop->last) <b class="text-gray-400">[FINAL]b> @endif
li>
@endforeach
ul>
section>
{{-- 4. Forelse (Manejo de estados vacíos) --}}
<section class="mb-8">
<h2 class="text-lg font-semibold">Etiquetas del perfil:h2>
<div class="flex gap-2">
@forelse($tags as $tag)
<span class="bg-gray-200 px-2 py-1">{{ $tag }}span>
@empty
<p class="text-gray-400 italic">No has definido etiquetas todavía.p>
@endforelse
div>
section>
{{-- 5. Directivas de Sesión y Errores (Útiles en formularios) --}}
<section class="mb-8 border-t pt-4">
@error('email')
<div class="text-red-500 font-bold">Error en el correo: {{ $message }}div>
@enderror
{{-- Directiva de entorno --}}
@env('local')
<div class="text-xs text-orange-400 mt-4 italic">
* Estás viendo esto porque el entorno es Local.
div>
@endenv
section>
{{-- 7. No toques nada de lo que hay aquí dentro, ignora las llaves {{ }} y déjalas tal cual --}}
@verbatim
<div id="app">
<h1>Perfil de Usuarioh1>
<p>Nombre: {{ user.name }}p>
<p>Email: {{ user.email }}p>
<p>Bio: {{ user.bio }}p>
div>
@endverbatim
Hola, @{{ nombre_en_js }}.
Tu edad es @{{ edad_en_js }}.
Tu ciudad es @{{ ciudad_en_js }}.
{{-- 8. PHP nativo (Solo cuando es estrictamente necesario) --}}
@php
$currentTime = now()->format('H:i');
@endphp
<p class="mt-10 text-xs">Generado a las: {{ $currentTime }}p>
@foreach($courses as $c)
<div>
<h3>{{ $c['name'] }}h3>
<canvas id="grafico-{{ $c['id'] }}">canvas>
div>
{{-- Usamos ONCE para que el script de la librería no se repita 100 veces --}}
@once
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js">script>
<script>
console.log("La librería Chart.js se cargó solo una vez.");
script>
@endpush
@endonce
{{-- Este PUSH no tiene ONCE porque cada gráfico sí necesita su propia inicialización --}}
@push('scripts')
<script>
new Chart(document.getElementById('grafico-{{ $c["id"] }}'), { ... });
script>
@endpush
@endforeach
@endsection
@push('scripts')
<script>
console.log('Blade ha renderizado la vista correctamente.');
script>
@endpushLo que más me gusta, más allá de toda esta integración, es que Blade también ha mejorado muchísimo.
Cada vez lo siento más parecido a Vue, lo cual me encanta, obviamente.
Impresión de HTML
Todo comienza con una simple impresión de datos desde el servidor, igual que en Vue (lo menciono porque es el framework que más uso):
{{ $name }}Aunque Angular, React, Astro, etc., también funcionan de forma similar en esta parte básica.
Además de las impresiones, Blade tiene condicionales, y lo que me gusta es que también hay condicionales específicos, como por ejemplo @auth o @guest, para verificar si el usuario está autenticado o es invitado.
Estructuras de Control y Condicionales
Blade hace que el PHP sea mucho más expresivo y limpio en el HTML.
Condicionales @if y @switch
Podemos emplear el @if, @else y @elseif de toda la vida.
{{-- 1. Condicionales If / Else --}}
<section class="mb-8">
<h2 class="text-lg font-semibold">Estado de la cuenta:h2>
@if($status === 1)
<span class="text-yellow-600">Esperando verificación...span>
@elseif($status === 2)
<span class="text-green-600">Cuenta verificada correctamente.span>
@else
<span class="text-red-600">Cuenta restringida.span>
@endif
section>Recuerda que puedes usar la triple igualdad (===) para comparar también por tipo, lo cual es muy útil en PHP al ser un lenguaje débilmente tipado (donde un string puede compararse con un entero).
@switch: Es ideal cuando tienes múltiples casos, como los roles de usuario (admin, editor, lector).
{{-- 2. Directiva Switch --}}
class="mb-8">
@switch($role)
@case('admin')
<div class="bg-blue-100 p-2 text-blue-800">Acceso total de administradordiv>
@break
@case('editor')
<div class="bg-green-100 p-2 text-green-800">Acceso de edicióndiv>
@break
@default
<div class="bg-gray-100 p-2">Acceso de lecturadiv>
@endswitch
También hay directivas como @empty, @isset, entre otras. Estas "muletillas" hacen que el código sea mucho más limpio y fácil de leer:
@isset($records)
// $records is defined and is not null...
@endisset
@empty($records)
// $records is "empty"...
@endemptyClaro que todo esto se puede hacer con un if clásico y funciones de PHP, pero esto es más elegante, especialmente cuando estás en Blade y necesitas mostrar contenido condicional de forma sencilla y legible.
El bucle @foreach y la variable $loop
En un @foreach, Laravel nos regala la variable mágica $loop. Con ella puedes saber:
- $loop->first: Si es el primer elemento (útil para poner etiquetas como "[NUEVO]").
- $loop->last: Si es el último elemento.
- $loop->iteration: El índice actual del recorrido.
{{-- 3. Bucles y la variable $loop --}}
<section class="mb-8">
<h2 class="text-xl font-bold mb-3">Listado de Cursosh2>
<ul class="space-y-2">
@foreach($courses as $course)
<li @class([
'p-3 rounded border',
'bg-yellow-50 border-yellow-200' => $course['premium'],
'bg-white border-gray-200' => !$course['premium']
])>
<strong>#{{ $loop->iteration }}strong> - {{ $course['name'] }}
<span class="text-sm italic">({{ $course['type'] }})span>
@if($loop->first) <b class="text-blue-500">[NUEVO]b> @endif
@if($loop->last) <b class="text-gray-400">[FINAL]b> @endif
li>
@endforeach
ul>
section>El potente @forelse
Es la solución perfecta para listados. Si la colección tiene datos, los itera; si está vacía, ejecuta el bloque @empty. Esto evita que la interfaz se vea mal o vacía cuando no hay contenido.
{{-- 4. Forelse (Manejo de estados vacíos) --}}
<section class="mb-8">
<h2 class="text-lg font-semibold">Etiquetas del perfil:h2>
<div class="flex gap-2">
@forelse($tags as $tag)
<span class="bg-gray-200 px-2 py-1">{{ $tag }}span>
@empty
<p class="text-gray-400 italic">No has definido etiquetas todavía.p>
@endforelse
div>
section>Manejo de errores y formularios
Con la directiva @error puedes mostrar errores de forma directa:
{{-- 5. Directivas de Sesión y Errores (Útiles en formularios) --}}
<section class="mb-8 border-t pt-4">
@error('email')
<div class="text-red-500 font-bold">Error en el correo: {{ $message }}div>
@enderrorEsto simplifica mucho el manejo de validaciones en formularios.
Variables de entorno
Puedes usar @env para detectar el entorno:
{{-- Directiva de entorno --}}
@env('local')
<div class="text-xs text-orange-400 mt-4 italic">
* Estás viendo esto porque el entorno es Local.
div>
@endenvIntegración con frontend (@verbatim)
Cuando trabajas con frameworks como Vue, puedes usar @verbatim para evitar conflictos con las llaves {{ }}.
Esto es muy útil cuando el renderizado lo hace el frontend.
Si usas frameworks como Vue o Alpine, puedes usar @verbatim para que Blade ignore las llaves {{ }} y permita que el framework de JavaScript las procese. También puedes usar @{{ }} para escapar una sola línea.
{{-- 7. No toques nada de lo que hay aquí dentro, ignora las llaves {{ }} y déjalas tal cual --}}
@verbatim
<div id="app">
<h1>Perfil de Usuarioh1>
<p>Nombre: {{ user.name }}p>
<p>Email: {{ user.email }}p>
<p>Bio: {{ user.bio }}p>
div>
@endverbatim
Hola, @{{ nombre_en_js }}.
Tu edad es @{{ edad_en_js }}.
Tu ciudad es @{{ ciudad_en_js }}.Uso de PHP dentro de Blade
Puedes usar @php, aunque con moderación:
@php
$currentTime = now()->format('H:i');
@endphpLo ideal es manejar la lógica en el controlador, pero en casos puntuales puede ser útil.
Directivas Avanzadas: Scripts y Stacks
La directiva @once permite ejecutar un bloque solo una vez, incluso dentro de bucles.
Esto es clave para evitar repetir scripts innecesariamente.
Junto con @push y @stack, puedes organizar mejor tus scripts:
- @push('scripts') agrega contenido
- @stack('scripts') lo renderiza en el layout
1. La directiva @once
Esta directiva garantiza que el código interno se renderice una sola vez, sin importar cuántas veces se repita un bucle. Es extremadamente útil para cargar librerías de JavaScript (como Chart.js) solo una vez, aunque tengas diez gráficos en la página.
2. @push y @stack
A diferencia de @yield, que suele usarse para un bloque único, el @stack funciona como una pila de libros.
- En el layout maestro defines el @stack('scripts').
- Desde cualquier vista o componente, usas @push('scripts').
Laravel irá apilando todos los scripts al final de la página, manteniendo todo organizado.
{{-- En el Layout --}}
@stack('scripts')
{{-- En cualquier parte de la vista --}}
@push('scripts')
<script>console.log("Este script se añade a la pila");script>
@endpushClases Dinámicas y Atributos
Una de las mejores adiciones recientes es la directiva @class. Es mucho más limpia y expresiva que hacer concatenaciones manuales de strings dentro de un class="".
Como te he comentado cuando hice un pequeño análisis de la versión 12 de Laravel, una de las cosas que más me gusta de esta nueva versión es que siento que todo está un poco más compenetrado:
Laravel YA NO es *solo* un Framework PHP
Ahora no solo tenemos, por ejemplo, Laravel, y si quieres emplear Vue tienes que usar un paquete con Jetstream, Inertia y todos esos "novelones", sino que ya todo se siente como parte de un mismo sistema.
Y ese “algo más” es una combinación bastante interesante de varias tecnologías, según lo que tú quieras usar.
- Si quieres emplear React, ya tienes un scaffolding preparado.
- Si quieres usar Vue, también está listo. Incluso hay proyectos de la comunidad (tema del que también hablé en otro video; si lo hice, te dejo la tarjeta aquí) donde puedes usar otros frameworks como Svelte, entre otros.
- Y obviamente también está Alpine. Creo que no lo mencioné antes, pero React, Alpine y Vue son los que vienen por defecto. Y por supuesto, Laravel puro, sin ningún agregado, o como yo lo llamo: Laravel Base.
O estilos:
'background-color: red',
'font-weight: bold' => $isActive,
])>
"background-color: red; font-weight: bold;">Atributos como checked, disabled, requerido o readonly:
<input
type="email"
name="email"
value="email@laravel.com"
@readonly($user->isNotAdmin())
/>
<input
type="checkbox"
name="active"
value="active"
@checked(old('active', $user->active))
/>
<button type="submit" @disabled($errors->isNotEmpty())>Submit
<input
type="text"
name="title"
value="title"
@required($user->isAdmin())
/>Seguridad y Escape de Datos HTML
HTML (HyperText Markup Language) es un lenguaje de marcado utilizado para crear páginas webs y por supuesto, esto incluye las aplicaciones web. Es un estándar que proporciona una estructura en base a una sintaxis basada en XML para organizar todo el contenido de una página web. HTML utilizando distintos tipos de etiquetas; HTML NO es un lenguaje de programación ya que, no se implementa ninguna lógica en HTML si no, un lenguaje de marcado en la cual, lo que tu coloques es lo que tu obtienes.
Imprimir HTML en blade
Muchas veces queremos mostrar un bloque HTML directamente en un archivo de vista blade; que por defecto, con:
{{ $html }}Para escapar el HTML, tenemos:
{!! $html !!}Y Recuerda pasar los datos desde el controlador:
class TestController extends Controller
{
public function index()
{
return view("index",['name' => 'Andres Cruz','html' => 'Titulo
']);
}
}Nunca uses esto con contenido que venga de usuarios (como comentarios).
La Directiva @include y sus Variantes
La forma más básica es el @include a secas. Por defecto, la vista que traigas va a "heredar" todas las variables que ya tengas en la página principal, pero también puedes pasarle datos extra si lo necesitas.
{{-- Incluimos una vista de errores y le pasamos un dato adicional --}}
<div>
@include('shared.errors', ['tipo' => 'critico'])
<form>
form>
div>Controlando la Existencia y Condiciones
A veces no estamos seguros de si una vista existe (por ejemplo, si permites temas personalizados) o queremos mostrarla solo si se cumple algo. Para eso tenemos estas joyas:
- @includeIf: Si la vista no existe, Laravel simplemente no hace nada en vez de lanzarte un error 500 a la cara.
- @includeWhen: Se incluye solo si la condición es verdadera.
- @includeUnless: Lo opuesto; se incluye solo si la condición es falsa.
- @includeFirst: Le pasas un array de vistas y Laravel renderizará la primera que encuentre. ¡Buenísimo para personalizaciones!
{{-- Solo si el usuario es admin, incluimos el panel de control --}}
@includeWhen($user->isAdmin(), 'admin.panel', ['status' => 'complete'])
{{-- Intenta cargar el diseño personalizado, si no, usa el de por defecto --}}
@includeFirst(['custom.header', 'defaults.header'], ['status' => 'complete'])@includeIsolated: Blindando tu Vista
Esto es nuevo y muy potente. Si quieres que la vista que estás incluyendo no sepa nada de lo que pasa en la vista padre (para evitar efectos colaterales o que se mezclen variables), usa @includeIsolated. Solo verá lo que tú le pases explícitamente.
{{-- Esta vista NO verá las variables del controlador, solo al $user --}}
@includeIsolated('view.name', ['user' => $user])Renderizando Colecciones con @each
Si tienes un listado (como los cursos o los posts que vimos antes), en vez de hacer un @foreach y adentro un @include, puedes hacerlo todo en una sola línea con @each.
Es súper eficiente y te permite incluso definir una vista para cuando el listado esté vacío (como el @empty del @forelse).
{{-- 1. Vista a renderizar, 2. Colección, 3. Variable en la vista, 4. Vista vacía --}}
@each('partials.job_card', $jobs, 'job', 'partials.no_jobs')