Skip to main content

Installation

npm install @sanity-labs/logo-soup
Logo Soup has optional peer dependencies. For Vue:
npm install vue@^3.5
Requires Vue 3.5+ for MaybeRefOrGetter support

Quick Start

The useLogoSoup composable handles normalization and reactivity:
<script setup>
import { ref } from "vue";
import { useLogoSoup } from "@sanity-labs/logo-soup/vue";
import { getVisualCenterTransform } from "@sanity-labs/logo-soup";

const logos = ref([
  { src: "/logos/acme.svg", alt: "Acme Corp" },
  { src: "/logos/globex.svg", alt: "Globex" },
]);

const { isLoading, isReady, normalizedLogos } = useLogoSoup({ logos });
</script>

<template>
  <div v-if="isReady">
    <img
      v-for="logo in normalizedLogos"
      :key="logo.src"
      :src="logo.src"
      :alt="logo.alt"
      :width="logo.normalizedWidth"
      :height="logo.normalizedHeight"
      :style="{
        transform: getVisualCenterTransform(logo, 'visual-center-y'),
      }"
    />
  </div>
</template>

Composable API

useLogoSoup

Options: All options accept ref, plain values, or getter functions (MaybeRefOrGetter). The composable automatically tracks dependencies and re-processes when any option changes.
type UseLogoSoupOptions = {
  logos: MaybeRefOrGetter<(string | LogoSource)[]>;
  baseSize?: MaybeRefOrGetter<number | undefined>;
  scaleFactor?: MaybeRefOrGetter<number | undefined>;
  contrastThreshold?: MaybeRefOrGetter<number | undefined>;
  densityAware?: MaybeRefOrGetter<boolean | undefined>;
  densityFactor?: MaybeRefOrGetter<number | undefined>;
  cropToContent?: MaybeRefOrGetter<boolean | undefined>;
  backgroundColor?: MaybeRefOrGetter<BackgroundColor | undefined>;
};
Return Value:
type UseLogoSoupReturn = {
  state: ShallowRef<LogoSoupState>;
  isLoading: ComputedRef<boolean>;
  isReady: ComputedRef<boolean>;
  normalizedLogos: ComputedRef<NormalizedLogo[]>;
  error: ComputedRef<Error | null>;
};

Reactive Options

The composable uses toValue() internally, so you can pass refs, reactive objects, or getter functions:
<script setup>
import { ref, computed } from "vue";
import { useLogoSoup } from "@sanity-labs/logo-soup/vue";

const logos = ref(["/logo1.svg", "/logo2.svg"]);
const size = ref(48);

// All these work:
const { normalizedLogos } = useLogoSoup({
  logos, // ref
  baseSize: size, // ref
  scaleFactor: 0.5, // plain value
  densityAware: () => size.value > 64, // getter function
});

// When logos or size changes, processing automatically re-runs
</script>

Examples

<script setup>
import { ref } from "vue";
import { useLogoSoup } from "@sanity-labs/logo-soup/vue";
import { getVisualCenterTransform } from "@sanity-labs/logo-soup";

const logos = ref([
  { src: "/logos/acme.svg", alt: "Acme Corp" },
  { src: "/logos/globex.svg", alt: "Globex" },
  { src: "/logos/initech.svg", alt: "Initech" },
]);

const { isReady, normalizedLogos } = useLogoSoup({ logos });
</script>

<template>
  <div v-if="isReady" class="flex gap-8 justify-center">
    <img
      v-for="logo in normalizedLogos"
      :key="logo.src"
      :src="logo.src"
      :alt="logo.alt"
      :width="logo.normalizedWidth"
      :height="logo.normalizedHeight"
      :style="{
        transform: getVisualCenterTransform(logo, 'visual-center-y'),
      }"
    />
  </div>
</template>

TypeScript

All Vue exports are fully typed:
import type {
  UseLogoSoupOptions,
  UseLogoSoupReturn,
} from "@sanity-labs/logo-soup/vue";

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

How It Works

The Vue adapter uses:
  • shallowRef to store the engine state
  • watchEffect to auto-track reactive option changes
  • toValue() to unwrap refs, reactive objects, and getters
  • computed for derived boolean flags
  • onScopeDispose for automatic cleanup
When any reactive option changes, watchEffect automatically re-runs and calls engine.process() with the new values.

See Also

Build docs developers (and LLMs) love