routeLoader$()

Route Loaders allow data to flow from the server to the Qwik Components. For this reason, Qwik routeLoaders should be always understood in the context of loading data in the server that is later consumed by a Qwik Component, if you want to create a RESTful endpoint, please check the endpoints guide instead.

They behave like RPC server-side functions that can be invoked by Qwik Components during rendering.

Declaring a loader

Route Loaders can only be declared inside the src/routes folder, in a layout.tsx or index.tsx file, and they MUST be exported.

// src/routes/layout.tsx
import { routeLoader$ } from '@builder.io/qwik-city';

export const getProductData = routeLoader$(async () => {
  // Add your data fetching code here (fetch, DB, Firebase, etc.)
  // const res = await fetch('https://.../product')
  // const products = await res.json()
  // return products;

  // We are returning sample data for this example
  return {
    product: {
      name: 'Qwik City',
      price: 100,
    },
  };
});

routeLoader$s are not endpoints, it's an internal communication channel between the server and the Qwik Components. Also, routeLoader$s must return a JSON serializable value.

Using a routeLoader$

routeLoader$s can be used by any component in the application, as long as the loader is declared in a layout.tsx or index.tsx file that is part of the existing route.

// src/routes/index.tsx
import { routeLoader$ } from '@builder.io/qwik-city';
import { component$ } from '@builder.io/qwik';

export const useGetServerTime = routeLoader$(async () => {
  return {
    time: Date.now(),
  }
});

export default component$(() => {
  // Retrieve a reactive signal of the loader data
  const signal = useGetServerTime(); // Signal<{time: number}>
  return (
    <div>
      Server time: {signal.value.time}
    </div>
  );
});

It may appear that useGetServerTime() initiates the retrieval of the server data when the component renders. But in reality, the routeLoader$ associated with useGetServerTime gets executed eagerly at HTTP request. In this way, the asynchronous work of fetching server data can initiate as early as possible. (routeLoader$ function invokes eagerly on HTTP request regardless of whether useGetServerTime() is invoked in the component.) For this reason, they are only allowed in the src/routes folder, in a layout.tsx or index.tsx files.

Multiple routeLoader$s

Multiple routeLoader$s are allowed across the whole application, and they can be used in any Qwik Component. You can even declare multiple routeLoader$s in the same file.

File: src/routes/layout.tsx

import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
import { Footer } from '../components/footer.tsx';

export const useGetServerTime = routeLoader$(async () => {
  return {
    time: Date.now(),
  }
});

export default component$(() => {
  const signal = useGetServerTime();
  return (
    <main>
      <Slot />
      <Footer />
    </main>
  );
});

File: src/components/footer.tsx

import { component$ } from '@builder.io/qwik';

// Import the loader from the layout
import { useGetServerTime } from '../routes/layout.tsx';

export const Footer = component$(() => {
  // Consume the loader data
  const signal = useGetServerTime();
  return <footer>Server time: {signal.value.time}</footer>;
});

The above example shows using useGetServerTime() in two different components across different files. This is intentional behavior.

File: src/routes/admin/index.tsx

import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';

export const useLoginStatus = routeLoader$(async ({cookie}) => {
  return {
    isUserLoggedIn: checkCookie(cookie);
  }
});

export const useCurrentUser = routeLoader$(async ({cookie}) => {
  return {
    user: currentUserFromCookie(cookie);
  }
});

export default component$(() => {
  const loginStatus = useLoginStatus();
  const currentUser = useCurrentUser();
  return (
    <section>
      <h1>Admin</h1>
      {loginStatus.value.isUserLoggedIn ? (
        <p>Welcome {currentUser.value.user.name}</p>
      ) : (
        <p>You are not logged in</p>
      )}
    </section>
  );
});

The above example shows two routeLoader$s being used in the same file. A generic useLoginStatus loader is used to check if the user is logged in, and a more specific useCurrentUser loader is used to retrieve the user data.

routeLoader context

Just like request handlers such as onRequest and onGet, routeLoader$s have access to the RequestEvent object which includes information about the current request.

This information comes in handy when the loader needs to conditionally return data based on the request, or it needs to override the response status, headers or body manually.

import { routeLoader$ } from '@builder.io/qwik-city';

export const useServerTime = routeLoader$(
  async ({headers, cookie, url, method, params}) => {
    console.log('Request headers:', headers);
    console.log('Request cookies:', cookie);
    console.log('Request url:', url);
    console.log('Request method:', method);
    console.log('Request params:', params);

    return {
      time: Date.now();
    }
  }
);

When to use a routeLoader?

A routeLoader should be used when you need to provide some server-side data to your Qwik Components. For example, if you need to fetch some data from a database or an API, you can use a routeLoader to do that.

You should not use a routeLoader to create a REST API, for that you’d be better off using an Endpoint, which allows you to have tight control over the response headers and body.

Made with ❤️ by

© 2023 Builder.io, Inc.