Tipografías e Imágenes en Next.js

Next.js ofrece herramientas poderosas para optimizar tanto tipografías como imágenes, mejorando significativamente el rendimiento y la experiencia del usuario.

1. Registro de Fuentes en Next.js

Configuración básica con next/font

Next.js 13+ incluye next/font que optimiza automáticamente las fuentes y elimina el layout shift.

tsx
// app/layout.tsx
import { Inter } from 'next/font/google'

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

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
  <html lang="es" className={inter.className}>
    <body>{children}</body>
  </html>
)
}

Configuración con variables CSS

tsx
// app/layout.tsx
import { Inter, Roboto_Mono } from 'next/font/google'

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

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

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
  <html lang="es" className={`${inter.variable} ${robotoMono.variable}`}>
    <body>{children}</body>
  </html>
)
}

2. Importar y Cargar Fuentes

Google Fonts

tsx
// lib/fonts.ts
import { 
Inter, 
Poppins, 
Source_Code_Pro,
Playfair_Display 
} from 'next/font/google'

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

export const poppins = Poppins({
weight: ['300', '400', '500', '600', '700'],
subsets: ['latin'],
display: 'swap',
variable: '--font-poppins',
})

export const sourceCodePro = Source_Code_Pro({
subsets: ['latin'],
display: 'swap',
variable: '--font-source-code-pro',
})

export const playfairDisplay = Playfair_Display({
subsets: ['latin'],
display: 'swap',
variable: '--font-playfair',
})

Fuentes locales

tsx
// lib/fonts.ts
import localFont from 'next/font/local'

export const customFont = localFont({
src: [
  {
    path: '../public/fonts/CustomFont-Regular.woff2',
    weight: '400',
    style: 'normal',
  },
  {
    path: '../public/fonts/CustomFont-Bold.woff2',
    weight: '700',
    style: 'normal',
  },
  {
    path: '../public/fonts/CustomFont-Italic.woff2',
    weight: '400',
    style: 'italic',
  },
],
variable: '--font-custom',
display: 'swap',
})

Usando las fuentes importadas

tsx
// app/layout.tsx
import { inter, poppins, customFont } from '../lib/fonts'

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
  <html 
    lang="es" 
    className={`${inter.variable} ${poppins.variable} ${customFont.variable}`}
  >
    <body className={inter.className}>
      {children}
    </body>
  </html>
)
}

3. Agregar Múltiples Fuentes al Proyecto

Configuración en CSS

css
/* globals.css */
:root {
--font-inter: 'Inter', sans-serif;
--font-poppins: 'Poppins', sans-serif;
--font-source-code-pro: 'Source Code Pro', monospace;
--font-playfair: 'Playfair Display', serif;
--font-custom: 'CustomFont', sans-serif;
}

/* Clases utilitarias */
.font-inter { font-family: var(--font-inter); }
.font-poppins { font-family: var(--font-poppins); }
.font-mono { font-family: var(--font-source-code-pro); }
.font-serif { font-family: var(--font-playfair); }
.font-custom { font-family: var(--font-custom); }

/* Configuración por defecto */
body {
font-family: var(--font-inter);
}

h1, h2, h3 {
font-family: var(--font-poppins);
}

code, pre {
font-family: var(--font-source-code-pro);
}

Uso en componentes

tsx
// components/Typography.tsx
import { inter, poppins, sourceCodePro } from '../lib/fonts'

export function Typography() {
return (
  <div>
    <h1 className={poppins.className}>
      Título con Poppins
    </h1>
    
    <p className={inter.className}>
      Párrafo con Inter para mejor legibilidad
    </p>
    
    <code className={sourceCodePro.className}>
      const code = "Source Code Pro";
    </code>
    
    {/* Usando variables CSS */}
    <div className="font-custom">
      Texto con fuente personalizada
    </div>
  </div>
)
}

4. Imágenes en Next.js

Componente Image básico

tsx
// components/Hero.tsx
import Image from 'next/image'

export function Hero() {
return (
  <div className="hero">
    <Image
      src="/hero-image.jpg"
      alt="Descripción de la imagen"
      width={800}
      height={600}
      priority
    />
  </div>
)
}

Imágenes responsivas

tsx
// components/ResponsiveImage.tsx
import Image from 'next/image'

export function ResponsiveImage() {
return (
  <div className="image-container">
    <Image
      src="/responsive-image.jpg"
      alt="Imagen responsiva"
      fill
      style={{
        objectFit: 'cover',
      }}
      sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
    />
  </div>
)
}

Galería de imágenes

tsx
// components/Gallery.tsx
import Image from 'next/image'

const images = [
{ src: '/gallery/image1.jpg', alt: 'Imagen 1' },
{ src: '/gallery/image2.jpg', alt: 'Imagen 2' },
{ src: '/gallery/image3.jpg', alt: 'Imagen 3' },
]

export function Gallery() {
return (
  <div className="gallery">
    {images.map((image, index) => (
      <div key={index} className="gallery-item">
        <Image
          src={image.src}
          alt={image.alt}
          width={400}
          height={300}
          loading={index < 2 ? 'eager' : 'lazy'}
          placeholder="blur"
          blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k="
        />
      </div>
    ))}
  </div>
)
}

5. Diferencia entre Image e img

Comparación práctica

tsx
// Componente tradicional img
function TraditionalImage() {
return (
  <img 
    src="/traditional-image.jpg" 
    alt="Imagen tradicional"
    width="800"
    height="600"
  />
)
}

// Componente Next.js Image
function OptimizedImage() {
return (
  <Image
    src="/optimized-image.jpg"
    alt="Imagen optimizada"
    width={800}
    height={600}
    placeholder="blur"
    blurDataURL="/placeholder.jpg"
  />
)
}

Tabla comparativa

Característica<img><Image>
Lazy LoadingManualAutomático
OptimizaciónNoSí (WebP, AVIF)
Layout ShiftPosiblePreviene
ResponsiveManualAutomático
PlaceholderNoBlur, shimmer
PerformanceBásicoOptimizado

6. Datos Importantes sobre el Componente Image

Configuración avanzada

tsx
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
  domains: ['example.com', 'cdn.example.com'],
  remotePatterns: [
    {
      protocol: 'https',
      hostname: '**.amazonaws.com',
      port: '',
      pathname: '/images/**',
    },
  ],
  formats: ['image/webp', 'image/avif'],
  deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
  imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  minimumCacheTTL: 60,
},
}

module.exports = nextConfig

Optimizaciones automáticas

tsx
// components/OptimizedGallery.tsx
import Image from 'next/image'

export function OptimizedGallery() {
return (
  <div className="optimized-gallery">
    {/* Imagen con prioridad para above-the-fold */}
    <Image
      src="/hero.jpg"
      alt="Imagen principal"
      width={1200}
      height={800}
      priority
      quality={90}
    />
    
    {/* Imagen con lazy loading */}
    <Image
      src="/content.jpg"
      alt="Contenido"
      width={600}
      height={400}
      loading="lazy"
      quality={75}
      placeholder="blur"
      blurDataURL="data:image/jpeg;base64,..."
    />
    
    {/* Imagen externa */}
    <Image
      src="https://cdn.example.com/image.jpg"
      alt="Imagen externa"
      width={400}
      height={300}
      unoptimized={false}
    />
  </div>
)
}

Estilos CSS para imágenes

css
/* styles/images.css */
.image-container {
position: relative;
width: 100%;
height: 400px;
}

.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1rem;
padding: 1rem;
}

.gallery-item {
position: relative;
aspect-ratio: 4/3;
overflow: hidden;
border-radius: 8px;
}

.gallery-item img {
transition: transform 0.3s ease;
}

.gallery-item:hover img {
transform: scale(1.05);
}

/* Responsive images */
@media (max-width: 768px) {
.image-container {
  height: 250px;
}

.gallery {
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
}

Mejores Prácticas

  Performance

Usa priority solo para imágenes above-the-fold y loading="lazy" para el resto. Configura quality entre 75-85 para balance óptimo.

  Tipografías

Combina máximo 2-3 familias tipográficas. Usa display: 'swap' para evitar FOIT y mejora la experiencia del usuario.

  SEO

Siempre incluye alt descriptivos en imágenes y usa width/height para prevenir layout shift y mejorar Core Web Vitals.

  Optimización

Configura remotePatterns para imágenes externas y usa formatos modernos (WebP, AVIF) para reducir el tamaño hasta 50%.