0Pricing
C# Academy · Lesson

Component Communication & DI

Pass data between parent and child components, use cascading parameters, and inject services into components.

Component Communication & DI is a free C# Academy lesson on CoddyKit — lesson 3 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, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Component Communication Patterns

Blazor components communicate in three directions: parent to child (Parameters), child to parent (EventCallback), and across the tree (CascadingParameters or shared services via DI).

Parent to Child: Parameters

Decorate a component property with [Parameter] to make it settable from the parent. The parent passes values as HTML attributes on the component tag.

@* Child: Alert.razor *@
<div class="alert alert-@Type">
    @Message
</div>

@code {
    [Parameter] public string Type { get; set; } = "info"; // primary, warning, danger
    [Parameter, EditorRequired] public string Message { get; set; } = "";
}

@* Parent usage: *@
<Alert Type="danger" Message="Something went wrong!" />
<Alert Message="Record saved." />

Child to Parent: EventCallback

A child component exposes an EventCallback parameter. The parent provides a handler method. The child invokes it when something happens.

@* Child: QuantityPicker.razor *@
<button @onclick="() => OnChanged.InvokeAsync(Value - 1)">-</button>
<span>@Value</span>
<button @onclick="() => OnChanged.InvokeAsync(Value + 1)">+</button>

@code {
    [Parameter] public int Value { get; set; } = 1;
    [Parameter] public EventCallback<int> OnChanged { get; set; }
}

@* Parent: *@
<QuantityPicker Value="@_qty" OnChanged="q => _qty = q" />
<p>Quantity: @_qty</p>

Sibling Communication via Parent

For sibling components to communicate, route data through the common parent. The parent holds shared state and passes it to children as parameters.

@* Parent holds shared state *@
<CategoryFilter OnChanged="c => _category = c" />
<ProductList Category="_category" />

@code {
    private string _category = "All";
}

@* CategoryFilter raises an event -> Parent updates _category
   -> ProductList re-renders with new Category *@

Cascading Parameters

Wrap a subtree in <CascadingValue> to pass data to all descendants without threading it through every intermediate component.

@* Wrap a subtree *@
<CascadingValue Value="_theme">
    <MainLayout />
</CascadingValue>

@code { private Theme _theme = new Theme { PrimaryColor = "#6366f1" }; }

@* Any descendant can receive it: *@
@code {
    [CascadingParameter]
    public Theme AppTheme { get; set; } = default!;
}

<h1 style="color: @AppTheme.PrimaryColor">Hello</h1>

Injecting Services with @inject

Use the @inject directive to inject registered DI services directly into components. They are resolved by the framework automatically.

@inject IProductService ProductSvc
@inject ILogger<ProductList> Logger
@inject NavigationManager Nav

@code {
    private List<Product> _products = new();

    protected override async Task OnInitializedAsync()
    {
        Logger.LogInformation("Loading products");
        _products = await ProductSvc.GetAllAsync();
    }

    private void GoToProduct(int id)
        => Nav.NavigateTo($"/products/{id}");
}

Scoped Services in Blazor Server

In Blazor Server, the DI scope lasts the entire SignalR connection lifetime — not just a single request. Be aware that scoped services are shared across all components in a circuit.

// Blazor Server: scoped = per circuit (connection lifetime)
builder.Services.AddScoped<CartService>();
// All components in the same browser tab share the SAME CartService

// This is GOOD for cart state (user's items persist across pages)
// This is BAD for request-scoped data — treat as session-like scope

// For true per-render-cycle scope, create your own:
using var scope = ScopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

NavigationManager for Routing

Inject NavigationManager to navigate programmatically, read the current URL, and handle navigation events.

@inject NavigationManager Nav

@code {
    private void GoHome()         => Nav.NavigateTo("/");
    private void GoToProduct(int id) => Nav.NavigateTo($"/products/{id}");
    private void ForceReload()    => Nav.NavigateTo("/", forceLoad: true);

    protected override void OnInitialized()
    {
        // Read current URL
        var uri = Nav.Uri;
        var path = new Uri(uri).AbsolutePath;
    }
}

HttpClient in Blazor WASM

In Blazor WebAssembly, inject a pre-configured HttpClient to call your API. Register it with a base address in Program.cs.

// Program.cs (Blazor WASM)
builder.Services.AddScoped(sp => new HttpClient
{
    BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});

// Component:
@inject HttpClient Http

@code {
    private List<Product> _products = new();

    protected override async Task OnInitializedAsync()
    {
        _products = await Http.GetFromJsonAsync<List<Product>>("/api/products")
                   ?? new();
    }
}

Real-World: User Context Service

A scoped UserContextService is injected into multiple components across the app — each sees the same user data without prop-drilling.

public class UserContextService
{
    public string? Name { get; private set; }
    public bool IsAdmin { get; private set; }

    public async Task InitializeAsync(AuthenticationState auth)
    {
        var user = auth.User;
        Name = user.Identity?.Name;
        IsAdmin = user.IsInRole("Admin");
    }
}

// Registered as scoped:
builder.Services.AddScoped<UserContextService>();

// Used in any component:
@inject UserContextService UserCtx
<p>Hello, @UserCtx.Name @(UserCtx.IsAdmin ? "(Admin)" : "")</p>

Quick Check

In Blazor Server, how long does a 'scoped' DI service lifetime last?

Recap: Component Communication & DI

Key takeaways:

  • Parent → Child: [Parameter] properties; [EditorRequired] for mandatory params
  • Child → Parent: EventCallback; auto-triggers parent StateHasChanged
  • Sibling: route through parent state or use a shared DI service
  • Cross-tree: CascadingValue/CascadingParameter for deep subtrees
  • @inject for services; Blazor Server scoped services last the circuit lifetime
  • NavigationManager for programmatic routing

Frequently asked questions

Is the “Component Communication & DI” lesson free?

Yes — the full text of “Component Communication & DI” is free to read here on the web, and the C# Academy course includes 4 lessons in total. 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.

What will I learn in “Component Communication & DI”?

Pass data between parent and child components, use cascading parameters, and inject services into components. 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; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Component Communication & DI” 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

  1. Blazor Component Model
  2. Data Binding & Event Handling
  3. Component Communication & DI
  4. State Management in Blazor
← Back to C# Academy