Blazor brings modern component-based architecture to .NET developers, enabling rich, interactive UIs using Razor components. But like any component model, communication between components is key.

In this article, we'll explore the three core techniques for communication in Blazor:

  1. EventCallback – for child-to-parent interaction
  2. CascadingParameter – for shared context across components
  3. @ref – for directly accessing methods and properties of child components

These techniques apply whether you're using Blazor WebAssembly or Blazor Server.


🧩 1. EventCallback – Raise Events from Child to Parent

This is the recommended way for a child component to notify its parent of a user action.

✅ Example: Product Selection

Child Component: ProductList.razor

@code {
    [Parameter] public EventCallback OnProductSelected { get; set; }

    private List products = new() {
        new() { Id = 1, Name = "Laptop" },
        new() { Id = 2, Name = "Phone" }
    };
}

    @foreach (var p in products)
    {
         OnProductSelected.InvokeAsync(p)">
            @p.Name
        
    }

Parent Page

@code {
    private Product? selectedProduct;
    private void HandleProductSelected(Product product)
    {
        selectedProduct = product;
    }
}

✅ Use this when the child needs to notify the parent of an action (like a click or submit).

🌐 2. CascadingParameter – Share Data Down the Tree

Use CascadingParameter when multiple nested components need access to shared state or context.

✅ Example: Shared Model

Parent Component

@code {
    private Product currentProduct = new() { Name = "Tablet", Description = "Shared model" };
}

Child Component: ProductDetails.razor

@code {
    [CascadingParameter] public Product? Product { get; set; }
}

@if (Product != null)
{
    @Product.Name - @Product.Description
}

📞 3. @ref – Call Methods on Child Components

Using @ref, the parent component can hold a reference to the child and call its public methods or access properties directly.

✅ Example: Editable Product List with Change Tracking

Child Component: ProductEditor.razor

@code {
    [Parameter] public List? Products { get; set; }

    private HashSet changedIds = new();

    public List GetChangedProducts() =>
        Products?.Where(p => changedIds.Contains(p.Id)).ToList() ?? new();

    public void MarkAsChanged(Product product)
    {
        changedIds.Add(product.Id);
        StateHasChanged();
    }

    private void OnNameChanged(Product p, string val)
    {
        p.Name = val;
        MarkAsChanged(p);
    }
}

Parent Page

Get Changed

@code {
    private ProductEditor? editorRef;
    private List products = new() {
        new() { Id = 1, Name = "Phone" },
        new() { Id = 2, Name = "Tablet" }
    };

    private void GetChanges()
    {
        var changed = editorRef?.GetChangedProducts();
    }
}

✅ Use this when the parent needs to trigger actions or pull state from the child.

🆚 When to Use What

Pattern Direction Use Case
EventCallback Child → Parent Notify parent of events (e.g., selection, form submit)
CascadingParameter Parent → Child Share context or models across many levels
@ref Parent → Child Imperatively access or control child behavior

🧠 Final Thoughts
Blazor gives you multiple tools to handle component communication. Choosing the right one depends on your direction of data flow, how many components are involved, and whether you're building reactive UIs or invoking imperative logic.

✅ Bonus: Combine Them!

You can (and often should) use multiple patterns together:

  • Use CascadingParameter for shared state
  • Use EventCallback to notify the parent
  • Use @ref when you need fine-grained control

🛠 Project Repo

Check out the full demo on GitHub:
👉 BlazorComponentComms