Dynamic Import and Lazy Loading

next/dynamic

Heavy client components that are not needed for the first paint (charts, maps, rich text editors, a "size guide" modal) can be split into their own chunk and loaded later:

'use client';

import dynamic from 'next/dynamic';
import { useState } from 'react';

const SizeGuide = dynamic(() => import('./SizeGuide'), {
  loading: () => <p>Loading size guide...</p>,
});
const StoreMap = dynamic(() => import('./StoreMap'), { ssr: false }); // uses window; client only

export default function ProductExtras() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <button onClick={() => setOpen(true)}>Size guide</button>
      {open && <SizeGuide />}
      <StoreMap />
    </>
  );
}

Note: ssr: false is only allowed inside Client Components. Server Components are already split automatically.

Load Libraries on Demand

'use client';

export default function ExportButton({ rows }: { rows: string[][] }) {
  async function exportExcel() {
    const XLSX = await import('xlsx'); // ~400 KB, downloaded only when clicked
    const ws = XLSX.utils.aoa_to_sheet(rows);
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, 'Orders');
    XLSX.writeFile(wb, 'orders.xlsx');
  }
  return <button onClick={exportExcel}>Export to Excel</button>;
}

Debounce Expensive Work

Example

Typing in a search box should not fire a request per key press. A typed debounce waits until the user pauses:

function debounce<A extends unknown[]>(fn: (...args: A) => void, ms: number) {
  let timer: ReturnType<typeof setTimeout> | undefined;
  return (...args: A) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), ms);
  };
}

const search = debounce((q: string) => console.log('API call: /api/search?q=' + q), 50);

// simulate typing "redmi" quickly, then a pause, then "redmi 13"
const keys = ['r', 're', 'red', 'redm', 'redmi'];
keys.forEach((q, i) => setTimeout(() => search(q), i * 10));
setTimeout(() => search('redmi 1'), 200);
setTimeout(() => search('redmi 13'), 210);

Output

API call: /api/search?q=redmi
API call: /api/search?q=redmi 13