Skip to content

8

Fetching Data
2026 Edition

In the previous chapter, you optimized image loading in your Qwik application. Now let's move to the dashboard and start loading data into the page.

In this chapter, you'll learn how to fetch dashboard data with routeLoader$(). The data comes from local TypeScript files, and the loading logic lives in dedicated helper functions.

🧭 Different ways to fetch data in Qwik

In Qwik, data can be loaded in different ways depending on where the logic should run and how the UI should receive the result.

  • API endpoints are useful when your frontend needs to call a dedicated server-side layer.
  • server$() lets you run server-only logic directly from Qwik.
  • routeLoader$() loads data for a route before the component renders.
  • useResource$() is useful when a component needs to handle asynchronous data directly in the UI. You'll use it later in the course.

These tools solve different problems. In this chapter, the practical focus is on routeLoader$(), because it is a strong fit for loading a dashboard page before rendering.

🧪 Local dashboard data files

For this chapter, you'll work with two local TypeScript files: one for the dashboard data and one for the TypeScript types used to describe that data.

Inside the src folder, create a data directory, then place the file here:

Inside the src folder, create a types directory, then place the file here:

Place them in the following locations:

  • src/data/placeholder-data.ts
  • src/types/placeholder-data-types.ts
💡

In this chapter, the data already exists. Your job is to load it cleanly with Qwik and pass it to the dashboard components.

The placeholder-data.ts file contains dashboard data such as customers, invoices, and revenue.

For example, each invoice object already has the shape you'll use throughout the dashboard:

src/data/placeholder-data.ts

The placeholder-data-types.ts file defines the TypeScript types used to describe the shape of that data.

For example, the Invoice type makes the expected structure explicit:

src/types/placeholder-data-types.ts

These types make the data shape clear before the data reaches your route or your components.

It’s time to take a quiz!

Test your knowledge and see what you’ve just learned.

What do these two local files provide for this chapter?

You must choose response !

📦 Preparing mocked dashboard data

First, inside the src folder, create a utils directory, then place the file here:

This file groups small reusable helpers for the dashboard, such as formatting currency, formatting dates, generating chart labels, and building pagination ranges.

Next, inside the src folder, create a lib directory, create a new file called loaders.ts, and add the following code:

src/lib/loaders.ts

Here are the main ideas in this file:

  • Keeping dashboard logic in one place: the helper functions centralize the data loading and shaping logic.
  • Fetching revenue data: fetchRevenue() returns the revenue data for the chart.
  • Fetching latest invoices: fetchLatestInvoices() sorts the invoices by date, takes the five most recent entries, joins them with the matching customer data, and formats the amount for display.
  • Fetching card data: fetchCardData() prepares the totals needed by the dashboard cards.
  • Using shared utilities: formatCurrency() comes from utils.ts and converts raw amounts into display-ready currency strings.

📈 Revenue chart

Fetching data for <RevenueChart/>

In src/routes/dashboard/layout.tsx, import the fetchRevenue() function you created in src/lib/loaders.ts.

In Qwik, you can use the routeLoader$() function to load data for a route before it renders.

Here, the loader calls fetchRevenue() and makes the result available to the dashboard route.

src/routes/dashboard/layout.tsx

In this snippet, useFetchRevenue() uses routeLoader$() to load the revenue data before the route renders.

routeLoader$() runs on the server, so the data is ready for your Qwik components when the page loads.

For this chapter, you declare and export the loader from src/routes/dashboard/layout.tsx, which makes the loaded data available across the dashboard route structure.

For more information about routeLoader$(), check the Qwik documentation.

Displaying data for <RevenueChart/>

Before updating the dashboard page, download the revenue-chart.tsx component and place it in src/components/ui/dashboard/.

This component is responsible for rendering the revenue chart from the data you pass to it.

To display data for the <RevenueChart/> component, import useFetchRevenue() from ./layout and use it inside src/routes/dashboard/index.tsx.

At this stage, the loader only provides revenue, so the page will only render the revenue chart for now.

src/routes/dashboard/index.tsx

Here, useFetchRevenue() gives the page access to the revenue data loaded in layout.tsx.

Then, check your localhost. You should be able to see a chart using the revenue data.

Revenue chart showing the total revenue for the last 12 months

Let's continue loading more dashboard data.

🧾 Latest invoices

Fetching data for <LatestInvoices/>

In src/routes/dashboard/layout.tsx, import the fetchLatestInvoices() function you created in src/lib/loaders.ts.

Just like the revenue data, you can load the latest invoices with routeLoader$() before the dashboard route renders.

Qwik allows multiple routeLoader$() functions in the same file. That works well here because each part of the dashboard can keep its own loader instead of being merged into one larger loader.

src/routes/dashboard/layout.tsx

Here, useFetchLatestInvoices() loads the latest invoice data for the dashboard route.

The loader returns the result of fetchLatestInvoices(), so the page can read that data with the loader hook.

At this point, layout.tsx exports one loader for revenue and one loader for the latest invoices.

Displaying data for <LatestInvoices/>

Before updating the dashboard page, download the latest-invoices.tsx component and place it in src/components/ui/dashboard/.

This component renders the latest invoices list, including the customer image, name, email, and formatted amount.

To display the customer images used in the latest invoices section, download customers.zip, unzip it, and place the extracted folder in the public folder.

To display data for the <LatestInvoices/> component, import useFetchLatestInvoices() from ./layout and use it inside src/routes/dashboard/index.tsx.

The page can now read both the revenue data and the latest invoices loaded by the route loaders.

src/routes/dashboard/index.tsx

Here, useFetchLatestInvoices() gives the page access to the latest invoice data loaded in layout.tsx.

The <LatestInvoices/> component then receives that data through its latestInvoices prop.

Then, check your localhost. You should now be able to see the latest invoices displayed on the dashboard.

Latest invoices section displaying customer images, names, emails, and invoice amounts

🃏 Dashboard cards

Now it's your turn to fetch data for the <Card> components. The cards will display the following data:

  • Total amount of invoices collected.
  • Total amount of invoices pending.
  • Total number of invoices.
  • Total number of customers.

The shaping logic already lives in fetchCardData().

Instead of calculating these values directly inside the page component, keep the logic inside src/lib/loaders.ts and load the result with routeLoader$().

The function you need to use is called fetchCardData(). It returns the values needed by the dashboard cards.

Hint:

  • Check the loaders.ts file to see what fetchCardData() returns.
  • Import fetchCardData() into your layout.tsx file and use it in a routeLoader$().
  • Use that loader in index.tsx to display the data in the <Card> components.

To complete this practice, download the cards.tsx component and place it in src/components/ui/dashboard/.

This component renders the summary cards at the top of the dashboard. Your job here is to load the values it needs and pass them in.

Once you're ready, expand the toggle below for the final code:

Great. You've now fetched all the data for the dashboard overview page.

Your page should look like this:

Dashboard page displaying cards, a revenue chart, and the latest invoices

⚡ Parallel dashboard loading

At this point, your dashboard uses three separate routeLoader$() functions:

  • useFetchRevenue()
  • useFetchLatestInvoices()
  • useFetchCardData()

This is a good fit here because these three data needs are independent.

Each loader does one job, keeps the code easier to read, and avoids turning the dashboard into one larger loader with mixed responsibilities.

For comparison, this is what the same dashboard logic would look like if those independent requests were awaited one after another inside a single loader:

Comparison example

This kind of step-by-step loading is often called a request waterfall.

A waterfall is useful when one request depends on the result of another. But that is not the case here. The revenue chart, the latest invoices, and the dashboard cards can all be loaded independently.

If you wanted to keep a single loader, you would usually start those independent requests in parallel with Promise.all():

Comparison example

For this dashboard page, the recommended approach remains the one used above: separate routeLoader$() functions for each independent data need.

It’s time to take a quiz!

Test your knowledge and see what you’ve just learned.

Why are three separate routeLoader$() functions a good fit for this dashboard page?

You must choose response !

This chapter gave you a clean foundation for the dashboard page: route loaders fetch the data, helper functions shape it, and each component receives only what it needs.

Here are a few useful references if you want to explore route-based data loading in more detail.

Source code

You can find the source code for chapter 8 2026 Edition on GitHub.

You've Completed Chapter 8

You've learned how to load dashboard data with routeLoader$().

Next Up

9: Optimizing Data Fetching

Learn how to move loaders to the right route boundary and trigger requests only when needed.