Skip to content
LoadingView the code for this module.

The load function runs before the component is created on both the client and server, allowing you to fetch and manipulate data before rendering the page, meaning you wont need to show a loading state onMount. Let's look at an example.

Back in our [name].svelte file, we are fetching data from our endpoint. Since the endpoint has the same file name as our page, this page receives the data returned from this endpoint directly as props. But what if we were to change our endpoint's name to [handle].json.js? Now, we no longer receive data from this endpoint as a prop. This endpoint is no longer a page endpoint, it is now a standalone endpoint, so what we should do instead is fetch our data using the load function.

In order to use the load function, the first thing we need to so is add a script tag with module context. Anything defined on the module scope will be evaluated only once when the module first evaluates, rather than for each component instance.

<script module="context"></script>

Now from this script tag, we can export the load function like this.

<script module="context">
  export async function load() {}
</script>

Load gives us a bunch of arguments that will help us fetch data. In this example we'll want access to this params object, which contains any dynamic params that were part of our request, and also a fetch function to actually fetch our data in both the client and the sever.

<script module="context">
  export async function load({(params, fetch)}) {}
</script>

Now, since the load function is reactive, it will re-run anytime one of its parameters changes if that param is being used in the function. In our example, since we will be using params, our load function will re-run anytime our dynamic param, name, changes. It's also important to use the SvelteKit-provided fetch wrapper rather than using the native fetch within our load function to avoid issues that may occur when calling the native fetch on the server.

Now, we can use our dynamic param, name, to fetch the appropriate data from our endpoint like this.

<script context="module">
  export async function load({ params, fetch }) {
    const response = await fetch(`/product/${params.handle}.json`);
    const product = await response.json();
  }
</script>

This alone will throw an error because the load function must return something. We can return a props object, which is an object that will pass data from the module script to the component as properties. In this example, it will return our product data.

<script context="module">
  export async function load({ params, fetch }) {
    const response = await fetch(`/product/${params.handle}.json`);
    const product = await response.json();

    return {
      props: {
        product: product.product,
      },
    };
  }
</script>

Now our load function is returning the data fetched from our endpoint, but the page still won't load. Since we changed our endpoint's dynamic param from name to handle in the beginning of this module, we'll have to update this in our endpoint as well. Within our [handle.json.js endpoint, update the code to use handle anywhere name is being used.

All of the code within our load function will run on both the client and the server, before the component is created, and we will now have product available to us in our component as a prop. If you watched my previous video on components, you may remember that in order to access props in a component we need to export that prop to make it available for assignment. It's important to remember to export our prop in our normal script tag like this.

<script context="module">
  export async function load({ params, fetch }) {
    const response = await fetch(`/product/${params.handle}.json`);
    const product = await response.json();

    return {
      props: {
        product: product.product,
      },
    };
  }
</script>

<script>
  export let product;
</script>

Now, our product's data is successfully being returned and will be displayed in our page. It's also important to point out that the load function will run anytime a parameter being used within it changes. This means if our product name changes, load() will run again before re-mounting the component.

Now, let's dive into how exactly this function works. This load function will be called before the component is created, and it will fetch our product list data from /product/[handle].json, which is an our endpoint. When we initially request this page from the server, all of this code in the load function is run on the server where it renders the HTML with our data. But this code also runs on the client. To prove this we can log product in our console. If we refresh the page, we will see product logged in the browser's console. This is because the code is running on the client. However, we will also see product logged in the terminal. That's because the load function is being run on the server as well. What's interesting is if we go to a different route and click back to /product/shirt, we will see that product is once again being logged in our browser's console, but not in the terminal. Why is this?

To best explain what's happening, let's take a look at a diagram.

When the page initially loads, the client requests /product/shirt from the server. The server then calls load, which fetches /product/shirt.json from the endpoint. However, the data does not need to be served from your app. It can just as easily be fetched from an external API.

The server then uses the returned data to render the HTML. In this particular example, the load function is calling fetch. It's important to note, that if you call fetch in load, the resulting data is inlined into the HTML.

When the browser hydrates the HTML, load is immediately called again, this time from the browser. However, this time fetch won't hit the network, because it reads the inlined data instead. Only calling fetch once guarantees consistency between server and client, and is a more efficient use of bandwidth.

This explains why we see product logged in both the browser and terminal when we refresh the page (because it is getting called twice, once on the server and once on the client). But you may be wondering, why call it twice? Why not just call it once and serialize the output of load? By calling load on both sides, we are not restricted as to what we can load. For example, you can conditionally 'load' different components, even though they couldn't be serialized.


Now we have a decent understanding of how load works when we first load the page. But if you remember, on subsequent page loads, we only see product being logged in the browser's console. It no longer is logged in the terminal. We can test this out by routing to a different page and then clicking back into /product/cup.

Why is this happening? Shouldn't load be getting called twice? Once on the client and on the server? Well, after the initial page load, any subsequent page loads will call load directly from the client. And this time, any fetch calls will hit the network as depicted in this diagram.

Now is also a good time to point out that load() is very different than one of Svelte's lifecycle functions, onMount. Every component has a lifecycle that starts when it is created, and ends when it is destroyed. load() will run before the component is rendered to the DOM, and the component will not be rendered until we receive a response from load(). This means that we do not need to display any sort of loading indicator. onMount() is different because it runs as soon as the component is mounted to the DOM. This means that the user will see the component before we receive a response from onMount(). load() should typically be used when you are fetching data, but there are use cases where you may need to use onMount() over load(), so it is important to understand the difference.

In our example, we use load() to fetch the product data. Now, what if we want to display a modal component that alerts the item is on sale, but only after the user has been on the page for five seconds? We can do this using the onMount function. Here, I've already added our modal component to this page which will be displayed if showModal is true. First we need to import onMount from Svelte. Now, let's create a new variable, seconds, which will start off as zero. In our onMount function we will set an interval that will increment seconds by one every second. Within this interval let's set showModal to true if seconds is 5. I'll also log the value of seconds.

<script context="module">
    export async function load({params, fetch}) {
        const response = await fetch(`/product/${params.handle}.json`);
        const product = await response.json()

        return {
            props: {
                product: product.product
            }
        }
    }
</script>

<script>
    import { onMount } from 'svelte';
    export let product;

    let showModal = false;
  let count = 0;

    onMount(() => {
       const interval = setInterval(() => {
            seconds += 1;
                console.log(seconds)
                if(seconds === 5) {
                        showModal = true;
                }
       }, 1000);
  });
</script>

{#if showModal}
    <Modal on:click={() => {showModal = false}}>
        <span slot="header">
            <div class="text-center font-bold uppercase mb-4">
                This item is on sale!
            </div>
        </span>
        <span slot="body">
            <div class="mb-4">
                {product.name} is on sale! It is only {product.price}
            </div>
        </span>
        <span slot="button">
            <button on:click={() => {showModal = false}} class="bg-pink-500 text-white uppercase font-medium p-2 rounded-md">Awesome!</button>
        </span>
    </Modal>
{/if}

Now, if we were to run this in the browser, we would see that every second our seconds variable is increased by one, and once it hits 5 we see our modal. Now, we should also clear our interval once our modal appears as well as when the component is unmounted. We can do this by returning a function like this.

onMount(() => {
  const interval = setInterval(() => {
    seconds += 1;
    console.log(seconds);
    if (seconds === 5) {
      showModal = true;
      clearInterval(interval);
    }
  }, 1000);
  return () => {
    clearInterval(interval);
  };
});

Anytime a function is returned from onMount, it will be called when the component is unmounted. It's also worth mentioning that unlike the load function, onMount can be used in all components. Not just pages.

Now that we know the correct way to load data into our app using the load function, in the next module we will learn how to prefetch our data to make our app feel extra snappy.