Otimização de imagens e assets no Next.js

1. Componente next/image e otimização automática

O Next.js oferece o componente Image como parte fundamental da estratégia de otimização de imagens. Este componente fornece otimização automática que inclui redimensionamento, conversão de formato e lazy loading nativo.

import Image from 'next/image';

export default function HeroBanner() {
  return (
    <div className="hero-container">
      <Image
        src="/images/banner-principal.jpg"
        alt="Banner principal do site"
        width={1200}
        height={600}
        priority
        placeholder="blur"
        blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..."
      />
    </div>
  );
}

O atributo priority desabilita o lazy loading para imagens acima da dobra, enquanto placeholder="blur" exibe uma versão miniatura borrada durante o carregamento. O Next.js converte automaticamente para formatos modernos como WebP e AVIF, baseando-se no suporte do navegador.

Para imagens responsivas, utilize o atributo sizes:

<Image
  src="/images/produto.jpg"
  alt="Produto em destaque"
  fill
  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
  className="object-cover"
/>

2. Configuração avançada do next.config.js para imagens

O arquivo de configuração do Next.js permite controle granular sobre o processamento de imagens. Para imagens externas, configure domínios permitidos:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'images.unsplash.com',
        port: '',
        pathname: '/**',
      },
      {
        protocol: 'https',
        hostname: 'cdn.suaempresa.com',
        port: '',
        pathname: '/uploads/**',
      },
    ],
    deviceSizes: [640, 768, 1024, 1280, 1536],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    formats: ['image/avif', 'image/webp'],
    minimumCacheTTL: 60,
  },
};

module.exports = nextConfig;

Para usar um loader customizado como Cloudinary:

// lib/cloudinary-loader.js
export default function cloudinaryLoader({ src, width, quality }) {
  const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`];
  return `https://res.cloudinary.com/seu-cloud/image/upload/${params.join(',')}/${src}`;
}

// Uso no componente
import Image from 'next/image';
import cloudinaryLoader from '@/lib/cloudinary-loader';

<Image
  loader={cloudinaryLoader}
  src="v123456/sua-imagem.jpg"
  alt="Imagem otimizada via Cloudinary"
  width={800}
  height={600}
/>

3. Otimização de fonts e CSS assets

O módulo next/font otimiza o carregamento de fontes com auto-subsetting e display=swap:

import { Inter, Roboto_Mono } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter',
});

const robotoMono = Roboto_Mono({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-roboto-mono',
});

export default function Layout({ children }) {
  return (
    <html lang="pt-BR" className={`${inter.variable} ${robotoMono.variable}`}>
      <body>{children}</body>
    </html>
  );
}

Para CSS, o Next.js suporta minificação automática via PostCSS. Configure plugins como cssnano para compressão adicional:

// postcss.config.js
module.exports = {
  plugins: {
    'postcss-import': {},
    'tailwindcss/nesting': {},
    tailwindcss: {},
    autoprefixer: {},
    ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}),
  },
};

4. Otimização de scripts e JavaScript bundles

O componente Script do Next.js oferece estratégias de carregamento inteligentes:

import Script from 'next/script';

export default function AnalyticsPage() {
  return (
    <>
      <Script
        src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', 'GA_MEASUREMENT_ID');
        `}
      </Script>
    </>
  );
}

Para code splitting de componentes pesados:

import dynamic from 'next/dynamic';

const DynamicChart = dynamic(() => import('@/components/GraficoComplexo'), {
  loading: () => <p>Carregando gráfico...</p>,
  ssr: false, // Desabilita SSR para componentes client-side
});

export default function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <DynamicChart />
    </div>
  );
}

Para análise de bundles:

// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

module.exports = withBundleAnalyzer(nextConfig);

Execute ANALYZE=true npm run build para visualizar o relatório interativo.

5. Cache e estratégias de entrega de assets

Configure headers de cache para assets estáticos:

// next.config.js
const nextConfig = {
  async headers() {
    return [
      {
        source: '/images/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=31536000, immutable',
          },
        ],
      },
      {
        source: '/fonts/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=31536000, immutable',
          },
        ],
      },
    ];
  },
};

Para ISR com cache inteligente:

export async function getStaticProps() {
  const data = await fetch('https://api.exemplo.com/produtos');
  const produtos = await data.json();

  return {
    props: { produtos },
    revalidate: 60, // Regenera a cada 60 segundos
  };
}

6. Otimização de SVGs e ícones

Configure o Webpack para importar SVGs como componentes React:

// next.config.js
const nextConfig = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      use: ['@svgr/webpack', 'url-loader'],
    });
    return config;
  },
};

Uso otimizado de SVGs:

import IconeUsuario from '@/assets/icones/usuario.svg';
import IconeCarrinho from '@/assets/icones/carrinho.svg';

export default function Header() {
  return (
    <nav>
      <IconeUsuario className="w-6 h-6" aria-hidden="true" />
      <IconeCarrinho className="w-6 h-6" aria-hidden="true" />
    </nav>
  );
}

Para compressão de SVGs com SVGO:

// next.config.js
const nextConfig = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      use: [
        {
          loader: '@svgr/webpack',
          options: {
            svgo: true,
            svgoConfig: {
              plugins: [
                { name: 'removeViewBox', active: false },
                { name: 'cleanupIDs', active: false },
              ],
            },
          },
        },
      ],
    });
    return config;
  },
};

7. Monitoramento e boas práticas de performance

Implemente monitoramento de Web Vitals:

// pages/_app.js
export function reportWebVitals(metric) {
  console.log(metric);

  if (metric.label === 'web-vital') {
    // Envie para sua analytics
    fetch('/api/vitals', {
      method: 'POST',
      body: JSON.stringify(metric),
    });
  }
}

Checklist final de otimização:

  1. Lazy Loading: Utilize loading="lazy" (padrão no next/image) para imagens abaixo da dobra
  2. Formatos Modernos: Configure formats: ['image/avif', 'image/webp'] no next.config.js
  3. Cache Agressivo: Defina Cache-Control: public, max-age=31536000, immutable para assets estáticos
  4. Compressão: Habilite compressão gzip/brotli no servidor ou CDN
  5. Acessibilidade: Sempre forneça alt descritivo e contraste adequado

Use o Lighthouse e o modo Turbo do Next.js para testar:

next dev --turbo

Monitore métricas como LCP (deve ser < 2.5s), CLS (< 0.1) e FID (< 100ms) para garantir que suas otimizações estão funcionando conforme esperado.

Referências