Capítulo 51 de 456

Lazy Loading

Core Idea

Reduz o JavaScript inicial de uma rota adiando o carregamento de Client Components e bibliotecas até serem necessários, via next/dynamic (composição de React.lazy() + Suspense) ou React.lazy() direto.

Key Concepts

  • next/dynamic: composto de React.lazy() + Suspense, funciona igual em app e pages.
  • ssr: false: desativa o prerendering (SSR) de um Client Component; só funciona dentro de Client Components, não em Server Components.
  • Server Components lazy import: importar dinamicamente um Server Component só faz lazy-load dos Client Components filhos, não do Server Component em si; também ajuda a preload de assets estáticos (CSS).
  • loading: opção do dynamic() para renderizar um componente de fallback enquanto o módulo carrega.
  • Named exports: dynamic import de export nomeado exige resolver via .then((mod) => mod.Nome).
  • Magic comments: webpackIgnore/turbopackIgnore (pula bundling, deixa import em runtime) e turbopackOptional (Turbopack only, suprime erro de build para módulo que pode não existir, mas lança MODULE_NOT_FOUND em runtime se ausente). Só funcionam com import()/require()/require.resolve()/new Worker() dinâmicos, não com import estático.

Code Examples

// app/page.js — Client Components dinâmicos
'use client'
import { useState } from 'react'
import dynamic from 'next/dynamic'

const ComponentA = dynamic(() => import('../components/A'))
const ComponentB = dynamic(() => import('../components/B'))
const ComponentC = dynamic(() => import('../components/C'), { ssr: false })

export default function ClientComponentExample() {
  const [showMore, setShowMore] = useState(false)
  return (
    <div>
      <ComponentA />
      {showMore && <ComponentB />}
      <button onClick={() => setShowMore(!showMore)}>Toggle</button>
      <ComponentC />
    </div>
  )
}
  • O que demonstra: carregamento imediato em bundle separado (A), sob demanda (B) e só no client sem SSR (C).
// Named export
const ClientComponent = dynamic(() =>
  import('../components/hello').then((mod) => mod.Hello)
)
  • O que demonstra: resolução de export nomeado via .then().
// Magic comments
const runtime = await import(/* webpackIgnore: true */ 'runtime-module')
const plugin = await import(/* turbopackIgnore: true */ pluginPath)
const feature = await import(/* turbopackOptional: true */ './optional-feature')
  • O que demonstra: controle do bundler sobre imports dinâmicos runtime-only ou opcionais.

Anti-patterns

  • ssr: false em Server Component: gera erro; a opção só é válida dentro de Client Components.
  • Esperar code splitting automático de Client Component importado por Server Component: não é suportado atualmente, dynamic import é necessário.
  • webpackOptional: não existe/não é suportado, usar turbopackOptional no Turbopack.

Key Takeaways

  1. Server Components já são code-split automaticamente; lazy loading via next/dynamic se aplica a Client Components e libs externas.
  2. Use ssr: false apenas dentro de Client Components para pular prerendering.
  3. Importar libs pesadas (ex.: fuse.js) sob demanda com import() dentro de handlers evita inflar o bundle inicial.
  4. loading no dynamic() melhora UX durante o carregamento assíncrono.
  5. Magic comments (webpackIgnore, turbopackIgnore, turbopackOptional) dão controle fino sobre bundling de imports dinâmicos.

Connects To

  • Streaming: complementa lazy loading no envio progressivo de UI do server pro client.
  • Server and Client Components: entender a fronteira é pré-requisito para saber onde ssr: false é válido.