Controlling When Components are Loaded in Nuxt

Explore how to optimize your Nuxt app’s performance by controlling when components load. Learn about lazy components, prefetching, and making components asynchronous to enhance user experience and reduce initial load times.

Michael Thiessen
Nuxt 3

Mastering Nuxt 3 course is here!

Get notified when we release new tutorials, lessons, and other expert Nuxt content.

Click here to view the Nuxt 3 course

We have a few different ways we can control when a component is loaded in our Nuxt apps.

This is really useful, because we don’t always want everything loaded on the initial page load — that can make things slow if we’ve got a lot going on in a single page.

Let’s take a look at three different aspects of Nuxt that give us more control.

Lazy Components

The first is by creating lazy components that are loaded asynchronously. The simplest way to do this is by adding the Lazy* prefix when using a component:

<template>
    <!-- Only fetch the Modal component when this is `true` -->
  <LazyModal v-if="showModal" />
</template>

This puts the Modal component in it’s own chunk, so it isn’t loaded on the initial page load. It will only be loaded when it needs to be rendered, when the v-if evaluates to true.

This can speed up our initial page load by a lot!

But there’s a problem here.

The benefit of using lazy components like this is the biggest when the component we’re lazy loading is huge. By moving a huge component that we don’t always need (or need immediately) out of our bundle we save a lot!

But when we do need it, it takes longer to fetch it.

Let’s say we have a modal component, and we only want to load it if the user is actually going to open it:

<template>
  <!-- Clicking this will fetch the Modal component -->
  <button @click="showModal = true">
      Open Modal
    </button>
  <LazyModal v-if="showModal" />
</template>

<script setup>
const showModal = ref(false);
</script>

If the Modal component is big enough, there will be a delay between clicking it and fetching it and then rendering it. And that makes for a poor UX.

That brings us to our second feature.

Prefetching Components

Luckily, Nuxt gives us a way to have finer control over when these lazy components get loaded. It’s called prefetchComponents, and we can use it to manually fetch components when we’re pretty sure they’ll be needed.

Using this method, we can wait to load the Modal so we don’t slow down our initial page load, but we can do it before we need to render the component so the user doesn’t notice any delay:

<template>
  <button
      @click="showModal = true"
      @mouseover="loadModal"
    >
      Open Modal
    </button>
  <LazyModal v-if="showModal" />
</template>

<script setup>
const showModal = ref(false);

async function loadModal() {
  // Load the Modal component on command
  await prefetchComponents('Modal');
}
</script>

With this implementation, we’ll start loading the Modal component as soon as the user puts their mouse over the button that will show it. This might only be a fraction of a second before they click the button, but that speed up might be all we need to vastly improve the UX.

Of course, you can trigger this prefetch however you want! For example, if the button lives further down the page, you could use an intersection observer to trigger the load when the button scrolls into the viewport.

Making components asynchronous

There are a few ways to make components asynchronous (which puts them in their own chunks), beyond using them with the Lazy* prefix.

If you want to create global components, you can do it using the *.global.vue suffix, which makes them async by default. You can also use the /components/global directory. By default, Nuxt loads all components inside of that directory as global components, and makes them async as well.

You can also configure a specific directory to always load components as async components. This is done through the components option in nuxt.config.ts:

export default defineNuxtConfig({
  components: [
    {
      path: '~/components/async',
      isAsync: true,
    },
    '~/components'
  ]
})

When isAsync is set to true, the component will always be loaded asynchronously, even if there is no Lazy* prefix used. This gives us the same behaviour as adding the prefix, but we don’t have to remember to write it that way.

This can be really useful if we have a large component that we always want to load asynchronously and don’t want to accidentally load synchronously.

You could also use the pattern field to specify a custom *.async.vue suffix:

export default defineNuxtConfig({
  components: [
    {
      path: '~/components',
      pattern: '*.async.vue',
      isAsync: true,
    },
    '~/components'
  ]
})

There are more configuration options you can find here.

Michael Thiessen
Michael is a passionate full time Vue.js and Nuxt.js educator. His weekly newsletter is sent to over 11,000 Vue developers, and he has written over a hundred articles for his blog and VueSchool.

Follow MasteringNuxt on