Skip to main content

Installation

npm install @sanity-labs/logo-soup
Logo Soup has optional peer dependencies. For React:
npm install react react-dom

Quick Start

The <LogoSoup> component provides a complete solution with built-in layout and alignment:
import { LogoSoup } from "@sanity-labs/logo-soup/react";

function LogoStrip() {
  return (
    <LogoSoup
      logos={[
        { src: "/logos/acme.svg", alt: "Acme Corp" },
        { src: "/logos/globex.svg", alt: "Globex" },
        { src: "/logos/initech.svg", alt: "Initech" },
      ]}
    />
  );
}

Component API

LogoSoup Component

The <LogoSoup> component accepts all processing options plus layout-specific props:
PropTypeDefaultDescription
logos(string | LogoSource)[]requiredArray of logo URLs or objects with src and alt
baseSizenumber48Target size in pixels
scaleFactornumber0.5Shape handling: 0 = uniform widths, 1 = uniform heights
densityAwarebooleantrueScale dense logos down and light logos up
densityFactornumber0.5How strong the density effect is (0-1)
cropToContentbooleanfalseCrop whitespace/padding from logos
contrastThresholdnumber10Minimum contrast for content detection
backgroundColorstring | [r,g,b]auto-detectedBackground color for contrast detection
gapnumber | string28Space between logos
alignByAlignmentMode"visual-center-y"Alignment mode (see below)
renderImage(props) => ReactNode<img>Custom image renderer
classNamestring-Container class name
styleCSSProperties-Container inline styles
onNormalized(logos) => void-Callback when normalization completes

Alignment Modes

ModeDescription
"bounds"Align by geometric center (bounding box)
"visual-center"Align by visual weight center (both axes)
"visual-center-x"Visual center horizontally only
"visual-center-y"Visual center vertically only (default)

Custom Image Component

Use renderImage to integrate with Next.js Image or add custom attributes:
import Image from "next/image";
import { LogoSoup } from "@sanity-labs/logo-soup/react";

function LogoStripWithNextImage() {
  return (
    <LogoSoup
      logos={logos}
      renderImage={(props) => (
        <Image
          src={props.src}
          alt={props.alt}
          width={props.width}
          height={props.height}
        />
      )}
    />
  );
}

useLogoSoup Hook

For custom layouts, use the useLogoSoup hook directly:
import { useLogoSoup } from "@sanity-labs/logo-soup/react";
import { getVisualCenterTransform } from "@sanity-labs/logo-soup";

function CustomGrid() {
  const { isLoading, normalizedLogos } = useLogoSoup({
    logos: ["/logo1.svg", "/logo2.svg"],
    baseSize: 48,
  });

  if (isLoading) return null;

  return (
    <div className="flex gap-4">
      {normalizedLogos.map((logo) => (
        <img
          key={logo.src}
          src={logo.src}
          alt={logo.alt}
          width={logo.normalizedWidth}
          height={logo.normalizedHeight}
          style={{
            transform: getVisualCenterTransform(logo, "visual-center-y"),
          }}
        />
      ))}
    </div>
  );
}

Hook API

Options:
type UseLogoSoupOptions = {
  logos: (string | LogoSource)[];
  baseSize?: number;
  scaleFactor?: number;
  contrastThreshold?: number;
  densityAware?: boolean;
  densityFactor?: number;
  cropToContent?: boolean;
  backgroundColor?: BackgroundColor;
};
Return Value:
type UseLogoSoupResult = {
  isLoading: boolean;
  isReady: boolean;
  normalizedLogos: NormalizedLogo[];
  error: Error | null;
};

Server-Side Rendering

The hook uses useSyncExternalStore with proper SSR support. During server rendering, it returns an idle state with empty logos to prevent hydration mismatches.
Logo processing happens client-side using canvas. The component will initially render without logos, then populate once measurements complete.

Examples

import { LogoSoup } from "@sanity-labs/logo-soup/react";

export function Partners() {
  return (
    <LogoSoup
      logos={[
        { src: "/logos/acme.svg", alt: "Acme Corp" },
        { src: "/logos/globex.svg", alt: "Globex" },
        { src: "/logos/initech.svg", alt: "Initech" },
      ]}
      baseSize={48}
      gap={32}
    />
  );
}

TypeScript

All React exports are fully typed:
import type {
  LogoSoupProps,
  UseLogoSoupOptions,
  UseLogoSoupResult,
  ImageRenderProps,
  RenderImageFn,
} from "@sanity-labs/logo-soup/react";

See Also

Build docs developers (and LLMs) love