React Integration

Learn how to use OPTIMAGE with React. Create reusable components for optimized responsive images.

Introduction

OPTIMAGE works seamlessly with React. Since OPTIMAGE serves standard image URLs over a CDN, no additional packages are required. Simply use the generated URLs in your components.

No dependencies required! OPTIMAGE provides standard image URLs that work with any React setup - Create React App, Vite, Next.js, Remix, and more.

Basic Usage

The simplest way to use OPTIMAGE in React is with a standard img element:

function Hero() {
  return (
    <img
      src="https://cdn.optimage.com/col_abc123/768/hero-banner.webp"
      srcSet={`
        https://cdn.optimage.com/col_abc123/320/hero-banner.webp 320w,
        https://cdn.optimage.com/col_abc123/768/hero-banner.webp 768w,
        https://cdn.optimage.com/col_abc123/1280/hero-banner.webp 1280w
      `}
      sizes="100vw"
      alt="Hero banner"
      loading="lazy"
    />
  );
}

OptimizedImage Component

Create a reusable component to simplify usage across your application:

// components/OptimizedImage.tsx
import { ImgHTMLAttributes } from 'react';

const SIZES = [320, 384, 448, 512, 576, 672, 768, 896, 1024, 1152, 1280];
const CDN_URL = 'https://cdn.optimage.com';

interface OptimizedImageProps extends ImgHTMLAttributes<HTMLImageElement> {
  collectionId: string;
  slug: string;
  alt: string;
  sizes?: string;
  priority?: boolean;
}

export function OptimizedImage({
  collectionId,
  slug,
  alt,
  sizes = '100vw',
  priority = false,
  className,
  ...props
}: OptimizedImageProps) {
  const srcSet = SIZES
    .map(size => `${CDN_URL}/${collectionId}/${size}/${slug}.webp ${size}w`)
    .join(', ');

  const src = `${CDN_URL}/${collectionId}/768/${slug}.webp`;

  return (
    <img
      src={src}
      srcSet={srcSet}
      sizes={sizes}
      alt={alt}
      loading={priority ? 'eager' : 'lazy'}
      decoding={priority ? 'sync' : 'async'}
      className={className}
      {...props}
    />
  );
}

Usage

import { OptimizedImage } from './components/OptimizedImage';

function ProductCard({ product }) {
  return (
    <div className="product-card">
      <OptimizedImage
        collectionId="col_products"
        slug={product.imageSlug}
        alt={product.name}
        sizes="(max-width: 768px) 100vw, 33vw"
        className="w-full h-48 object-cover rounded-lg"
      />
      <h3>{product.name}</h3>
    </div>
  );
}

useOptimage Hook

For more flexibility, create a custom hook that generates URLs:

// hooks/useOptimage.ts
import { useMemo } from 'react';

const SIZES = [320, 384, 448, 512, 576, 672, 768, 896, 1024, 1152, 1280];
const CDN_URL = 'https://cdn.optimage.com';

export function useOptimage(collectionId: string, slug: string) {
  return useMemo(() => {
    const getUrl = (size: number) =>
      `${CDN_URL}/${collectionId}/${size}/${slug}.webp`;

    const srcSet = SIZES
      .map(size => `${getUrl(size)} ${size}w`)
      .join(', ');

    return {
      src: getUrl(768),
      srcSet,
      getUrl,
    };
  }, [collectionId, slug]);
}

Usage

import { useOptimage } from './hooks/useOptimage';

function Avatar({ userId, name }) {
  const { src, srcSet, getUrl } = useOptimage('col_avatars', userId);

  return (
    <div>
      {/* Responsive image */}
      <img
        src={src}
        srcSet={srcSet}
        sizes="64px"
        alt={name}
        className="w-16 h-16 rounded-full"
      />

      {/* Fixed size thumbnail */}
      <img
        src={getUrl(320)}
        alt={name}
        className="w-8 h-8 rounded-full"
      />
    </div>
  );
}

Next.js Integration

If you're using Next.js, you can configure the Image component to work with OPTIMAGE:

next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'cdn.optimage.com',
        pathname: '/**',
      },
    ],
  },
};

module.exports = nextConfig;

Using next/image

import Image from 'next/image';

function Hero() {
  return (
    <Image
      src="https://cdn.optimage.com/col_abc123/1280/hero.webp"
      alt="Hero banner"
      width={1280}
      height={720}
      priority
      className="w-full h-auto"
    />
  );
}

Note: When using next/image, Next.js handles optimization on its own. You may prefer using the standard img element with OPTIMAGE's pre-optimized images to avoid double optimization and leverage the full srcset.

TypeScript Types

Define types for your OPTIMAGE integration:

// types/optimage.ts
export type OptimageSize =
  | 320 | 384 | 448 | 512 | 576
  | 672 | 768 | 896 | 1024 | 1152 | 1280;

export interface OptimageImage {
  collectionId: string;
  slug: string;
}

export const OPTIMAGE_CONFIG = {
  cdnUrl: 'https://cdn.optimage.com',
  sizes: [320, 384, 448, 512, 576, 672, 768, 896, 1024, 1152, 1280],
  defaultSize: 768,
};

Best Practices

  • 1.Use appropriate sizes attribute: Match the sizes attribute to your layout to help browsers pick the right image.
  • 2.Set priority for above-the-fold images: Use loading="eager" for hero images and LCP elements.
  • 3.Add width and height: Prevent layout shift by specifying dimensions or using aspect-ratio CSS.
  • 4.Use meaningful alt text: Always provide descriptive alt attributes for accessibility.

Complete Example with Best Practices

function ProductGallery({ products }) {
  return (
    <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
      {products.map((product, index) => (
        <OptimizedImage
          key={product.id}
          collectionId="col_products"
          slug={product.slug}
          alt={`${product.name} - ${product.description}`}
          sizes="(max-width: 768px) 100vw, 33vw"
          priority={index < 3} // First 3 images load eagerly
          className="w-full aspect-square object-cover rounded-lg"
          style={{ aspectRatio: '1 / 1' }}
        />
      ))}
    </div>
  );
}