Nuxt3 Prerendering: How to wait on async content

About a 4 minute read

Written by on

#javascript #nuxt #vue

I’ve recently gone on the journey of learning Nuxt; specifically, Nuxt3. I’ve used VueJS before, in fact, that’s what I used for randomanime.org. However, I kept the site “multi-page” and simply plugged in the Vue components where I needed the dynamic aspect. My reason for wanting to learn Nuxt is pretty simple: I wanted to remake my personal site and start writing again.

The fact that you are reading this, means I did it! This site is running on Nuxt3. Besides just learning, another reason I chose Nuxt is because of the built in prerendering it can do, and that leads me to what I want to write about today.

How to prerender

After you have your Nuxt project setup, prerending is as easy as one little command:

npm run generate

Just like that, the application is built, the prerendering engine runs, and you have your prerendered site located under the dist directory.

Main prerendering issue

The main issue I ran into is, with this site, that my articles and stories needed to first make a request to get the content before it could be displayed. However, I discovered the prerendering engine wasn’t waiting for the content to load.

Here’s how I was fetching my data:

async function fetchData(isPrerender: boolean = false) {
    return await $fetch(`/api/post?handle=${route.params.handle}`)
        .then((resp: any) => {
            resp = JSON.parse(resp);
            return resp.results;
        })
        .catch(() => {});
}

Pretty simple fetch, right? Or I guess, $fetch.

I spent a lot of time Googling how to make the prerendering engine wait, but ended up discovering the answer by accident in the documentation.

Making the prerender wait

The answer for my question wasn’t actually in the Nuxt documentation, but rather Vue’s. If you click the link, you will find Vue’s composition API and, specifically, the onServerPrefetch lifecycle hook.

From that page, this specific hook “Registers an async function to be resolved before the component instance is to be rendered on the server.”. So that means, we can use this hook to grab our async content, and it’ll wait until the promise is resolved to actually render the page. Nice!!

All I needed to do was use the onServerPrefetch hook, call my fetchData function from earlier, and resolve the promise once that data has come back.

onServerPrefetch(() => {
    return new Promise(async (res) => {
        post.value = await fetchData(true);
        res("");
    });
});

TADA! Now after I look at my pages after prerendering, I can see the content within the source. Is this necessary for crawlers to read the post correctly? I’m not sure, but I didn’t want to take any chances.

Thanks for reading, and I hope this was helpful to someone out there.