Skip to main content

Installation

npm install @sanity-labs/logo-soup
Logo Soup has optional peer dependencies. For Solid:
npm install solid-js@^1.9

Quick Start

The useLogoSoup primitive integrates with Solid’s fine-grained reactivity:
import { useLogoSoup } from "@sanity-labs/logo-soup/solid";
import { getVisualCenterTransform } from "@sanity-labs/logo-soup";
import { For, Show } from "solid-js";

function LogoStrip() {
  const result = useLogoSoup(() => ({
    logos: ["/logos/acme.svg", "/logos/globex.svg"],
  }));

  return (
    <Show when={result.isReady}>
      <For each={result.normalizedLogos}>
        {(logo) => (
          <img
            src={logo.src}
            alt={logo.alt}
            width={logo.normalizedWidth}
            height={logo.normalizedHeight}
            style={{
              transform: getVisualCenterTransform(logo, "visual-center-y"),
            }}
          />
        )}
      </For>
    </Show>
  );
}

API

useLogoSoup

Signature:
function useLogoSoup(optionsFn: () => ProcessOptions): UseLogoSoupResult
Options: A getter function that returns process options. Solid tracks reactive dependencies inside this function.
type ProcessOptions = {
  logos: (string | LogoSource)[];
  baseSize?: number;
  scaleFactor?: number;
  contrastThreshold?: number;
  densityAware?: boolean;
  densityFactor?: number;
  cropToContent?: boolean;
  backgroundColor?: BackgroundColor;
};
Return Value:
type UseLogoSoupResult = {
  readonly isLoading: boolean;
  readonly isReady: boolean;
  readonly normalizedLogos: NormalizedLogo[];
  readonly error: Error | null;
};
All return properties are reactive getters. Solid automatically tracks when you access them, so components re-render when values change.

Reactive Options

Pass a getter function that reads signals. When any signal changes, Solid re-runs the getter and triggers re-processing:
import { createSignal } from "solid-js";
import { useLogoSoup } from "@sanity-labs/logo-soup/solid";

function DynamicLogos() {
  const [logos, setLogos] = createSignal(["/logo1.svg", "/logo2.svg"]);
  const [size, setSize] = createSignal(48);

  // Solid tracks logos() and size() reads inside this getter
  const result = useLogoSoup(() => ({
    logos: logos(),
    baseSize: size(),
  }));

  // When logos or size change, processing automatically re-runs
  return (
    <div>
      <button onClick={() => setSize(size() + 8)}>Increase Size</button>
      {/* ... render logos ... */}
    </div>
  );
}

Examples

import { useLogoSoup } from "@sanity-labs/logo-soup/solid";
import { getVisualCenterTransform } from "@sanity-labs/logo-soup";
import { For, Show } from "solid-js";

function LogoStrip() {
  const result = useLogoSoup(() => ({
    logos: [
      { src: "/logos/acme.svg", alt: "Acme Corp" },
      { src: "/logos/globex.svg", alt: "Globex" },
      { src: "/logos/initech.svg", alt: "Initech" },
    ],
    baseSize: 48,
  }));

  return (
    <Show when={result.isReady}>
      <div style={{ display: "flex", gap: "2rem", "justify-content": "center" }}>
        <For each={result.normalizedLogos}>
          {(logo) => (
            <img
              src={logo.src}
              alt={logo.alt}
              width={logo.normalizedWidth}
              height={logo.normalizedHeight}
              style={{
                transform: getVisualCenterTransform(logo, "visual-center-y"),
              }}
            />
          )}
        </For>
      </div>
    </Show>
  );
}

TypeScript

All Solid exports are fully typed:
import type {
  UseLogoSoupResult,
} from "@sanity-labs/logo-soup/solid";

import type {
  ProcessOptions,
  LogoSource,
  NormalizedLogo,
  AlignmentMode,
  BackgroundColor,
} from "@sanity-labs/logo-soup";

How It Works

The Solid adapter uses:
  • from() to convert the engine’s subscribe/getSnapshot into a signal
  • createEffect to re-run processing when the options getter’s dependencies change
  • Reactive getters on the return object so Solid can track fine-grained updates
  • onCleanup for automatic engine cleanup
import { from, createEffect, onCleanup } from "solid-js";
import { createLogoSoup as createEngine } from "../core/create-logo-soup";

export function useLogoSoup(optionsFn: () => ProcessOptions) {
  const engine = createEngine();
  
  // from() creates a signal from the engine's subscribe/getSnapshot
  const state = from<LogoSoupState>((set) => {
    set(engine.getSnapshot());
    return engine.subscribe(() => set(engine.getSnapshot()));
  });
  
  // createEffect re-runs when dependencies inside optionsFn() change
  createEffect(() => {
    engine.process(optionsFn());
  });
  
  onCleanup(() => engine.destroy());
  
  return {
    get isLoading() {
      return (state() ?? IDLE_STATE).status === "loading";
    },
    get normalizedLogos() {
      return (state() ?? IDLE_STATE).normalizedLogos;
    },
    // ... other getters
  };
}

See Also

Build docs developers (and LLMs) love