Skip to content

Last updated:

Lazy Loading

Lazy loading reduces the initial bundle size by splitting code into smaller chunks that are loaded on demand. Nuxt 4 provides automatic code splitting for routes, and Vue offers defineAsyncComponent for component-level splitting.

Automatic Route-Based Code Splitting

Nuxt automatically splits the bundle by route. Each page in app/pages/ becomes its own chunk that is only downloaded when the user navigates to that route.

app/pages/
  index.vue            → chunk for /
  login.vue            → chunk for /login
  patients/
    index.vue          → chunk for /patients
    [id].vue           → chunk for /patients/:id
  transactions/
    index.vue          → chunk for /transactions
  queue/
    index.vue          → chunk for /queue

No configuration is needed -- this is a built-in Nuxt behavior. When a clinical staff member logs in and goes to the queue page, only the queue page's JavaScript is loaded. The transactions, patient registration, and pharmacy pages are not downloaded until they are actually visited.

Lazy Loading Components with defineAsyncComponent

For heavy components that are not immediately visible (modals, charts, complex forms), use defineAsyncComponent to defer loading:

vue
<script setup lang="ts">
import { defineAsyncComponent } from 'vue';

// This component's code is only downloaded when the modal opens
const PatientHistoryForm = defineAsyncComponent(
  () => import('~/components/patient/PatientHistoryForm.vue')
);

const showHistory = ref(false);
</script>

<template>
  <UiButton @click="showHistory = true">View History</UiButton>

  <UiModal v-model="showHistory" title="Patient History">
    <!-- Component loads only when modal is open -->
    <PatientHistoryForm v-if="showHistory" :patient-id="patientId" />
  </UiModal>
</template>

With Loading and Error States

For larger components, provide feedback during loading:

vue
<script setup lang="ts">
const SurgeryScheduleCalendar = defineAsyncComponent({
  loader: () => import('~/components/surgery/SurgeryScheduleCalendar.vue'),
  loadingComponent: UiSkeletonLoader,
  errorComponent: UiAlert,
  delay: 200,       // Show loading after 200ms
  timeout: 10000,   // Timeout after 10s
});
</script>

Nuxt Lazy Component Prefix

Nuxt provides a Lazy prefix convention for auto-imported components. Prefixing a component name with Lazy automatically wraps it in defineAsyncComponent:

vue
<template>
  <!-- Eagerly loaded -- downloaded with the page chunk -->
  <UiButton>Save</UiButton>

  <!-- Lazily loaded -- downloaded only when rendered -->
  <LazyPatientBillingHistory
    v-if="showBilling"
    :patient-id="patientId"
  />
</template>

This works because Nuxt's components configuration in nuxt.config.ts auto-imports all components in app/components/:

ts
// nuxt.config.ts
components: [
  {
    path: '~/components',
    pathPrefix: false,
  },
],

Dynamic Imports for Heavy Dependencies

If a page uses a large third-party library (for example, a charting library for dashboard analytics), import it dynamically:

ts
// Only download the chart library when the dashboard is viewed
const loadChart = async () => {
  const { Chart } = await import('chart.js');
  // Use Chart...
};

onMounted(() => {
  if (showDashboard.value) {
    loadChart();
  }
});

NuxtLink automatically prefetches the target page's chunk when the link enters the viewport. This makes navigation feel instant because the code is already downloaded by the time the user clicks.

vue
<template>
  <!-- Nuxt prefetches /patients chunk when this link is visible -->
  <NuxtLink to="/patients">Patient Records</NuxtLink>

  <!-- Disable prefetching for rarely-visited pages -->
  <NuxtLink to="/admin/settings" :prefetch="false">Settings</NuxtLink>
</template>

When to Use Lazy Loading

ScenarioApproach
Page routesAutomatic (Nuxt handles it)
Modal content (patient history, forms)defineAsyncComponent or Lazy prefix
Tab content not visible on loadv-if + Lazy prefix
Heavy third-party librariesDynamic import()
Components always visible on pageDo not lazy load (adds latency)

What Not to Lazy Load

  • UI primitives (UiButton, UiInput, UiTable) -- These are small, used everywhere, and should be in the main bundle.
  • Layout components -- Always visible and needed immediately.
  • Components above the fold -- Lazy loading these causes layout shift and a worse perceived performance.

Verifying Code Splitting

You can inspect the generated chunks during build:

bash
npx nuxt build

# Check the output in .output/public/_nuxt/
# Each route and lazy component will have its own .js chunk

Use browser DevTools Network tab to verify that chunks are loaded on demand as you navigate between pages.

CPR - Clinical Patient Records