blazor-layouts-plantillas-maestras

Layouts en Blazor: diseños comunes y anidados

  • 3 min

Un layout es un componente que define la estructura común alrededor del contenido de una página.

Así puedes reutilizar una barra de navegación, un pie o un menú lateral sin copiarlos en cada .razor. El layout reserva un hueco mediante @Body para renderizar el contenido específico.

Anatomía de un layout

Técnicamente, un Layout es un componente Razor normal, pero con dos particularidades:

  1. Hereda de la clase base LayoutComponentBase.
  2. Utiliza la propiedad @Body para indicar dónde debe renderizarse el contenido.

Vamos a ver un ejemplo minimalista de un archivo MyLayout.razor:

@inherits LayoutComponentBase

<div class="mi-contenedor-principal">

    <header>
        <h1>Mi Aplicación Increíble</h1>
    </header>

    <div class="contenido-central">
        @Body
    </div>

    <footer>
        <p>&copy; @DateTime.Now.Year - Luis Llamas</p>
    </footer>

</div>
Copied!

Cuando el usuario navegue a /home, Blazor cogerá el contenido de Home.razor y lo “pegará” exactamente donde pusimos @Body.

El archivo MainLayout.razor

Cuando creas un proyecto nuevo en Blazor, verás que en la carpeta Components/Layout ya existe un archivo llamado MainLayout.razor.

Este es el diseño por defecto de tu aplicación. Si lo abres, verás algo parecido a esto:

@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <main>
        <div class="top-row px-4">
            <a href="...">About</a>
        </div>

        <article class="content px-4">
            @Body
        </article>
    </main>
</div>
Copied!

Aquí vemos cómo el Layout utiliza otros componentes normales (<NavMenu />) para construir la barra lateral. Esto demuestra que los Layouts son totalmente componibles.

Aplicar layouts

¿Cómo sabe Blazor qué Layout debe usar para cada página? Tenemos dos formas de definirlo: Global y Específica.

Asignación predeterminada con DefaultLayout

En el archivo Routes.razor (el enrutador que vimos en los primeros artículos), se define cuál es el Layout por defecto para toda la aplicación si no se especifica lo contrario.

<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
Copied!

Esto es muy cómodo: creas una página nueva y automáticamente hereda el diseño maestro.

Asignación específica con @layout

A veces necesitamos romper la regla. Una página de login o una landing page puede necesitar un diseño sin menú lateral ni barra superior.

Para ello, creamos un layout alternativo (ej: EmptyLayout.razor) y lo forzamos en la página usando la directiva @layout.

@page "/login"
@layout EmptyLayout

<h3>Iniciar Sesión</h3>
@code { ... }
Copied!

Al hacer esto, esta página ignorará el MainLayout configurado en el Router y usará el EmptyLayout.

Layouts anidados

Supongamos que una aplicación tiene dos áreas diferenciadas:

  1. Área Pública: Diseño estándar.
  2. Panel de Administración: Necesita, además del diseño estándar, una barra de herramientas extra y un submenú de gestión.

No hace falta duplicar código. Podemos hacer que un Layout herede de otro.

AdminLayout.razor:

@inherits LayoutComponentBase
@layout MainLayout

<div class="admin-panel">

    <div class="admin-toolbar">
        <button>Crear Usuario</button>
        <button>Ver Logs</button>
    </div>

    <div class="admin-content">
        @Body
    </div>

</div>
Copied!

Ahora, cualquier página que use @layout AdminLayout tendrá:

  1. La estructura del MainLayout (Sidebar, Footer).
  2. Dentro, la estructura del AdminLayout (Toolbar).
  3. Y finalmente, el contenido de la página.

Es como una muñeca rusa (Matrioska) de componentes.

Modificar zonas del layout con secciones

Un problema clásico en versiones antiguas de Blazor era: “Tengo el título de la página <h1> en el Layout, pero quiero cambiar el texto dependiendo de la página en la que estoy”.

Desde .NET 8, esto se soluciona de forma elegante con los componentes SectionOutlet y SectionContent.

En el Layout (MainLayout.razor): Definimos un hueco (outlet) y le damos un nombre.

<header>
    <SectionOutlet SectionName="ActionButtons" />
</header>
Copied!

En la Página (ProductList.razor): Rellenamos ese hueco con contenido específico.

@page "/productos"

<h3>Listado</h3>

<SectionContent SectionName="ActionButtons">
    <button class="btn btn-primary">➕ Nuevo Producto</button>
</SectionContent>
Copied!

Esto permite mantener fija la estructura y dejar que cada página proporcione contenido para zonas concretas, como un título o los botones de la cabecera.