Blazor Component Model
Create Razor components, understand the component lifecycle, use parameters and cascading values.
Blazor Component Model is a free C# Academy lesson on CoddyKit. This is lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, and your progress syncs across the web and the CoddyKit app. The C# Academy course includes 4 lessons in total.
What Is Blazor?
Blazor lets you build interactive web UIs using C# instead of JavaScript. Components are defined in .razor files combining HTML markup and C# logic. Blazor runs on the server (Blazor Server) or in the browser via WebAssembly (Blazor WASM).
Your First Component
A Razor component is a .razor file with HTML markup and a @code block for C#. The component name becomes the HTML tag name.
@* Greeting.razor *@
<h2>Hello, @Name!</h2>
<p>You have @Count messages.</p>
@code {
[Parameter] public string Name { get; set; } = "World";
private int Count = 5;
}Component Lifecycle
Blazor components have a lifecycle with hooks you can override. The most common ones are OnInitializedAsync (data loading) and OnParametersSetAsync (reacting to parameter changes).
@code {
private List<Product> Products = new();
protected override async Task OnInitializedAsync()
{
// Called once on first render
Products = await Http.GetFromJsonAsync<List<Product>>("/api/products")
?? new();
}
protected override async Task OnParametersSetAsync()
{
// Called whenever parameters change
await LoadDataAsync();
}
}Parameters: Passing Data to Components
Decorate properties with [Parameter] to accept data from parent components. Parameters are set before render and can trigger re-render when changed.
@* ProductCard.razor *@
<div class="card">
<h4>@Product.Name</h4>
<p>Price: @Product.Price.ToString("C")</p>
<button @onclick="AddToCart">Add to Cart</button>
</div>
@code {
[Parameter, EditorRequired]
public Product Product { get; set; } = default!;
[Parameter]
public EventCallback<Product> OnAddToCart { get; set; }
private async Task AddToCart() =>
await OnAddToCart.InvokeAsync(Product);
}Cascading Parameters
Cascading parameters flow down the component tree without explicit passing through every intermediate level — ideal for themes, user info, or shared state.
@* App.razor — wraps the whole app *@
<CascadingValue Value="CurrentUser">
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
</Router>
</CascadingValue>
@* Anywhere in the tree: *@
@code {
[CascadingParameter]
public UserInfo CurrentUser { get; set; } = default!;
}Rendering Lists and Conditionals
Use Razor directives @if, @foreach, and @for to render dynamic content. Always provide @key on list items for efficient diffing.
@if (Products.Count == 0)
{
<p>No products found.</p>
}
else
{
<ul>
@foreach (var product in Products)
{
<li @key="product.Id">
<ProductCard Product="product" OnAddToCart="HandleAdd" />
</li>
}
</ul>
}RenderFragment and ChildContent
Use RenderFragment to pass markup as a parameter — like slots in Vue or children in React. The conventional parameter name is ChildContent.
@* Panel.razor *@
<div class="panel">
<div class="panel-header">@Title</div>
<div class="panel-body">@ChildContent</div>
</div>
@code {
[Parameter] public string Title { get; set; } = "";
[Parameter] public RenderFragment? ChildContent { get; set; }
}
@* Usage: *@
<Panel Title="Order Details">
<p>Order #42 — Shipped</p>
</Panel>Component References with @ref
Capture a reference to a child component with @ref to call its public methods or read its properties from the parent.
@* Parent.razor *@
<ConfirmDialog @ref="_dialog" OnConfirm="DeleteOrder" />
<button @onclick="() => _dialog!.Show()">Delete Order</button>
@code {
private ConfirmDialog? _dialog;
private async Task DeleteOrder()
{
await OrderService.DeleteAsync(CurrentOrderId);
// Refresh UI...
}
}StateHasChanged and Manual Re-Rendering
Blazor automatically re-renders after event handlers. For async state changes outside event handlers (e.g., from a background timer), call StateHasChanged() manually.
@implements IDisposable
<p>Time: @CurrentTime</p>
@code {
private string CurrentTime = "";
private Timer? _timer;
protected override void OnInitialized()
{
_timer = new Timer(_ =>
{
CurrentTime = DateTime.Now.ToString("HH:mm:ss");
InvokeAsync(StateHasChanged); // thread-safe re-render
}, null, 0, 1000);
}
public void Dispose() => _timer?.Dispose();
}ShouldRender for Performance
Override ShouldRender() to skip unnecessary re-renders. Return false when the component's output won't change, reducing diffing overhead.
@code {
private string _lastRenderedStatus = "";
[Parameter] public string Status { get; set; } = "";
protected override bool ShouldRender()
{
// Only re-render if Status actually changed
if (Status == _lastRenderedStatus) return false;
_lastRenderedStatus = Status;
return true;
}
}Real-World: Product Listing Component
A complete product listing component that loads data on initialization, handles loading state, and passes events to the parent.
@inject HttpClient Http
@if (_loading)
{
<p>Loading products...</p>
}
else
{
@foreach (var p in _products)
{
<ProductCard Product="p" OnAddToCart="AddToCart" />
}
}
@code {
private List<Product> _products = new();
private bool _loading = true;
[Parameter] public EventCallback<Product> OnCartUpdated { get; set; }
protected override async Task OnInitializedAsync()
{
_products = await Http.GetFromJsonAsync<List<Product>>("/api/products")
?? new();
_loading = false;
}
private async Task AddToCart(Product p) => await OnCartUpdated.InvokeAsync(p);
}Quick Check
Which lifecycle method is the best place to load data from an API when a Blazor component initializes?
Recap: Blazor Component Model
Key takeaways:
- .razor files mix HTML markup with C# in an @code block
- [Parameter] accepts data from parent; EventCallback passes events up
- CascadingParameter flows data down without prop-drilling
- RenderFragment/ChildContent creates composable slot-based components
- OnInitializedAsync for initial data load; StateHasChanged for manual re-render
- ShouldRender() skips unnecessary diffing for performance
Learn C# with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 93
- Lessons
- 346
Frequently Asked Questions
Is the “Blazor Component Model” lesson free?
Yes — the full text of “Blazor Component Model” is free to read here on the web. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO. The C# Academy course includes 4 lessons in total.
What will I learn in “Blazor Component Model”?
Create Razor components, understand the component lifecycle, use parameters and cascading values. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start C# Academy?
No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 1 of 4.
How long does the “Blazor Component Model” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this C# Academy lesson?
Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Blazor Component Model
- Data Binding & Event Handling
- Component Communication & DI
- State Management in Blazor