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.
In this chapter...
Here are the topics we will cover:
🧭 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.tssrc/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:
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:
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?
📦 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:
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 fromutils.tsand 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.
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.
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.

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.
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.
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.

🃏 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.tsfile to see whatfetchCardData()returns.- Import
fetchCardData()into yourlayout.tsxfile and use it in arouteLoader$().- Use that loader in
index.tsxto 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:

⚡ 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:
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():
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?
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.
Recommended reading
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.
Was this helpful?