Blazor desde cero: Capítulo 3 — Componentes que escalan
Bienvenido al Capítulo 3 de Blazor desde cero. Si te perdiste el Capítulo 2, léelo primero para tener el proyecto base listo.
En el Capítulo 2 hicimos que la app corriera. En este capítulo haremos que sea mantenible.
Las apps Blazor se vuelven caóticas rápido cuando cada página termina como un .razor gigante. Los componentes son la forma de evitarlo: te dan consistencia, reutilización y límites claros entre piezas de UI.
Qué es realmente un componente en Blazor
Un componente es un archivo .razor que puede:
- Renderizar marcado
- Mantener estado local
- Recibir datos por parámetros
- Emitir eventos al componente padre
- Renderizar contenido hijo
En runtime, Blazor trata cada componente como una pequeña máquina de estado. Cuando cambia el estado, vuelve a renderizar y aplica un diff del DOM.
Por eso tu objetivo es definir entradas limpias y comportamiento predecible.
Paso 1: Empieza con un componente enfocado
Crea Components/Common/SectionHeader.razor:
<header class="section-header">
<h2>@Title</h2>
@if (!string.IsNullOrWhiteSpace(Subtitle))
{
<p>@Subtitle</p>
}
</header>
@code {
[Parameter] public string Title { get; set; } = string.Empty;
[Parameter] public string? Subtitle { get; set; }
}
Úsalo en Components/Pages/Home.razor:
@page "/"
<PageTitle>Blazor desde cero</PageTitle>
<SectionHeader
Title="Blazor desde cero"
Subtitle="El capítulo 3 trata sobre componentes." />
Es pequeño, pero introduce la idea más importante: un componente debería entenderse solo leyendo sus parámetros.
Paso 2: Usa parámetros para hacer explícito el comportamiento
Ahora, un botón reutilizable.
Components/Common/AppButton.razor:
<button class="app-button @VariantCssClass" @onclick="OnClick">
@Text
</button>
@code {
[Parameter] public string Text { get; set; } = "Button";
[Parameter] public string Variant { get; set; } = "primary";
[Parameter] public EventCallback OnClick { get; set; }
private string VariantCssClass => Variant.ToLowerInvariant() switch
{
"secondary" => "app-button--secondary",
"danger" => "app-button--danger",
_ => "app-button--primary"
};
}
Uso:
@code {
private int _savedCount;
private void Save()
{
_savedCount++;
}
}
<AppButton Text="Save" Variant="primary" OnClick="Save" />
<p>Guardado @_savedCount veces.</p>
Lo importante aquí es el contrato:
- Parámetros pocos y claros
- Nombres explícitos en lugar de comportamiento mágico
- Valores por defecto seguros
Paso 3: Usa RenderFragment para composición de layout
RenderFragment permite que el padre pase bloques de UI al hijo.
Crea Components/Common/Card.razor:
<article class="card">
<header class="card__header">@Title</header>
<section class="card__body">
@ChildContent
</section>
</article>
@code {
[Parameter] public string Title { get; set; } = string.Empty;
[Parameter] public RenderFragment? ChildContent { get; set; }
}
Uso:
<Card Title="Roadmap">
<ul>
<li>Componentes</li>
<li>Data binding</li>
<li>Routing</li>
</ul>
</Card>
Este patrón ayuda a construir estructuras consistentes sin repetir el mismo markup contenedor en cada página.
Paso 4: Prefiere composición sobre páginas gigantes
Si una página crece demasiado, sepárala por responsabilidad:
ProfileSummarypara el bloque principalProfileStatspara métricasProfileActivityListpara actividad reciente
La página principal queda como orquestación, no como detalle de implementación.
Paso 5: Equilibra markup y lógica
Para componentes simples, @code inline está bien.
Para componentes grandes, mueve lógica a code-behind:
UserCard.razorUserCard.razor.cs
Así el markup queda legible y la lógica C# más fácil de navegar.
Paso 6: Estructura de carpetas práctica
Una estructura que escala bien:
Components/Pages/-> páginas enrutableComponents/Layout/-> shell y navegaciónComponents/Common/-> bloques genéricos compartidosComponents/Features/<FeatureName>/-> componentes por funcionalidad
Errores comunes al empezar con Blazor
- Pasar demasiados parámetros en vez de usar un modelo dedicado
- Poner reglas de negocio dentro de páginas
- Crear un “componente dios” con cientos de líneas
- Repetir patrones de markup en vez de extraer piezas reutilizables
Próxima entrega
En el Capítulo 4 veremos data binding y eventos: @bind, manejo de eventos, tradeoffs del two-way binding y patrones para mantener un estado predecible.