Where to fetch data in your Blazor components

November 7, 2023 · 3 minute read · Tags: blazor

When you build a single page in Razor pages (or view in MVC), it’s usually obvious where to fetch your data.

The ’norm’ is to fetch the data you need for that entire page at the point it’s first served.

For example, say you need to show a list of products for an online store.

If you’re using Razor pages you’d probably retrieve the list of products via a DB call in the OnGet method.

public async Task OnGetAsync()
{
    Products = await _context.Products.ToListAsync();
}

But what if you’re using Razor Components.

Razor components encourage you to break your UI down into smaller puzzle pieces.

In this case you might choose to have a ProductCard component which shows the details of each product.

You might even go on to have separate components to show the price and whether or not a product has free delivery.

But now which of these components should fetch data?

You might be tempted to have each component fetch its own data.

A component tree diagram shows multiple components, each making a call to an API to fetch data

But I don’t recommend it (in most cases).

Instead I would always recommend minimising side-effects in components that form part of a page like this.

Side-effects are when your component interacts with resources “outside” itself, in this case, connecting to an API or DB.

The danger is we lose control how many API/DB calls we’re making once we display several products on the page.

For every product we display, our component tree ends up making numerous calls to an API or DB.

Each component fetches just the tiny piece of data it needs for its own UI and is largely ignorant of the calls being made by other components.

So it’s very easy to end up making hundreds of networks calls when one would have been more efficient.

Then there’s the problem of over-fetching, and under-fetching.

If a component fetches more data than it needs then it can be said to be over-fetching.

Equally, if we have lots of components only fetching the tiny piece of data they need, we can easily under-fetch, and end up making more DB/API calls elsewhere, to plug the gaps.

Keep it simple

My overwhelming drive here would be to do the “simplest thing”.

Simple in this case means fewer moving parts (fewer DB/API calls) and fewer places to have to look if we want to tweak our code to be more efficient.

For UI that’s homogenous (as here, where we’re effectively rendering a single page of related data) I’d be inclined to make one DB/API call at the top level.

In our Product list example that would mean making the API/DB call in the ProductList component, then using parameters to push the data down to the other components.

A component tree showing a top-lvel ProductList component which fetches its own data, then passes that down to other components via their parameters

With this we can have one highly-optimised query to fetch the data we’re going to display on this ‘page’ and all our other components can focus on their own UI/Logic.

When components have zero side-effects and only accept data in via parameters they’re typically a lot easier to reason about and maintain/extend.

It also makes it easier to ensure the child components re-render as the data changes.

Typically, if you fetch data in the top-level component and have children components which depend on that data (via parameters) the child components will re-render automatically when the top-level data changes.

You can find out exactly what leads to your components being re-rendered with my handy interactive guide here.

But the gist is, if your child components take a parameter that is a Reference type, they will generally re-render whenever the parent component re-renders.

Next up

How to upload a file with Blazor SSR in .NET 8?
How to handle file uploads without using an interactive render mode?
3 simple design tips to improve your Web UI
Spruce up your features
The quickest way to integrate PayPal checkout with Blazor SSR in .NET 8
JavaScript Interop works differently with Blazor Server-side rendering