Embed reference

One script tag, about 5KB gzipped, zero dependencies. It renders in a closed Shadow DOM (your site’s CSS can’t break it and it can’t break your site), reserves its height before logos load (zero layout shift), respects prefers-reduced-motion, and renders the required “Powered by LogoSyncer” badge on the free tier automatically. Don’t have a wall yet? Start with the quickstart.

The script tag

<script async
  src="https://logosyncer.com/embed/v1.js"
  data-wall="YOUR_WALL_PUBLIC_ID"
  data-style="grid"></script>

Your wall’s public_id comes from the dashboard’s Embed screen or GET /v1/walls. To try the embed before connecting Stripe, use data-wall="demo".

Style presets (data-style)

Ten named presets over three renderers (grid / row / marquee):

PresetWhat you get
gridResponsive multi-row grid (the default).
rowSingle static row, wraps on small screens.
marqueeContinuously scrolling single lane, pauses on hover.
two-row-marqueeTwo staggered marquee lanes scrolling in opposite directions.
mono-gridGrid with a monochrome filter — logos in one tone.
mono-marqueeMarquee with the same monochrome treatment.
darkGrid tuned for dark page backgrounds (light-variant logos where available).
lightGrid tuned for light page backgrounds.
compactTighter spacing and smaller logos — sidebars, footers.
cardsEach logo in a bordered card tile.

Attributes

AttributeValuesDefaultNotes
data-wallwall public_idrequireddemo works without an account.
data-styleone of the 10 presets abovegridUnknown values fall back to grid.
data-displaylogo · logo-name · namelogologo shows logos only; logo-name shows the logo with the company name beside it; name renders text-only company names — useful when you want a typographic wall or logos aren’t resolving well for your customer set.
data-max-heightnumber (px)40Logo height cap, clamped to 200.
data-limitnumber60Maximum logos rendered, clamped to 500.

Example — a compact, name-annotated marquee capped at 24 logos:

<script async
  src="https://logosyncer.com/embed/v1.js"
  data-wall="w_x7k2mq3v9d1p"
  data-style="marquee"
  data-display="logo-name"
  data-max-height="32"
  data-limit="24"></script>

Custom rendering from wall JSON

If you’d rather own the markup (or a strict CSP blocks third-party scripts), fetch the wall JSON and render it yourself. Two rules: every string in the payload is third-party data — render it as text or alt attributes, never as HTML — and on the free tier keep a visible “Powered by LogoSyncer” link near the wall when meta.attribution_required is true. Keep meta.takedown_url reachable too (a small link near the wall or in your footer) — it is the public page where a listed company can report a listing.

React component

import { useEffect, useState } from "react";

const LOGOSYNCER_BASE = "https://logosyncer.com";

export function LogoWall(props) {
  const [logos, setLogos] = useState([]);
  const [takedownUrl, setTakedownUrl] = useState("");

  useEffect(() => {
    let cancelled = false;
    fetch(LOGOSYNCER_BASE + "/v1/walls/" + encodeURIComponent(props.wallId))
      .then((res) => (res.ok ? res.json() : null))
      .then((data) => {
        if (cancelled || !data) return;
        setLogos(data.logos);
        setTakedownUrl(data.meta.takedown_url);
      })
      .catch(() => undefined);
    return () => {
      cancelled = true;
    };
  }, [props.wallId]);

  // Wall JSON is third-party data: names go into alt text via JSX text
  // binding only (textContent-safe). Never inject wall strings as HTML.
  return (
    <div>
      <ul className="logo-wall">
        {logos.map((logo) => (
          <li key={logo.domain}>
            <img
              src={new URL(logo.logo.png, LOGOSYNCER_BASE).toString()}
              alt={logo.name}
              loading="lazy"
              height="40"
            />
          </li>
        ))}
      </ul>
      {takedownUrl ? (
        <a className="logo-wall-takedown" href={takedownUrl}>
          Logo removal requests
        </a>
      ) : null}
    </div>
  );
}

Companion CSS (plain HTML grid)

.logo-wall {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 24px;
  align-items: center;
  list-style: none;
  margin: 0;
  padding: 0;
}
.logo-wall img { max-height: 40px; width: auto; object-fit: contain; }
.logo-wall-takedown { font-size: 12px; opacity: 0.6; }

Tailwind grid variant

<ul className="grid grid-cols-2 gap-8 sm:grid-cols-3 lg:grid-cols-5 items-center list-none p-0 m-0">
  {logos.map((logo) => (
    <li key={logo.domain} className="flex justify-center">
      <img
        className="h-8 w-auto object-contain opacity-70 grayscale transition hover:opacity-100 hover:grayscale-0"
        src={new URL(logo.logo.png, LOGOSYNCER_BASE).toString()}
        alt={logo.name}
        loading="lazy"
      />
    </li>
  ))}
</ul>

Pure-CSS marquee (no JavaScript; render the list twice)

<div class="ls-marquee" role="img" aria-label="Customer logos">
  <div class="ls-marquee-track">
    <!-- one <img> per logo, list rendered TWICE back to back -->
    <img src="https://logosyncer.com/logo/acme.com.png" alt="Acme" height="32" />
    <img src="https://logosyncer.com/logo/globex.com.png" alt="Globex" height="32" />
    <!-- ...second copy of the same list, with alt="" aria-hidden="true"... -->
    <img src="https://logosyncer.com/logo/acme.com.png" alt="" aria-hidden="true" height="32" />
    <img src="https://logosyncer.com/logo/globex.com.png" alt="" aria-hidden="true" height="32" />
  </div>
</div>

<style>
  .ls-marquee { overflow: hidden; }
  .ls-marquee-track {
    display: flex;
    gap: 48px;
    align-items: center;
    width: max-content;
    animation: ls-scroll 30s linear infinite;
  }
  .ls-marquee-track img { height: 32px; width: auto; }
  @keyframes ls-scroll {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
  }
  @media (prefers-reduced-motion: reduce) {
    .ls-marquee-track { animation: none; flex-wrap: wrap; }
  }
</style>

Where to paste it on your platform

Copying the tag is easy; knowing where to paste it is the real blocker. Platform by platform:

  • Webflow: add an Embed element where the wall should appear and paste the script tag into it (site-wide alternative: Site settings → Custom code → Footer code). Publish to see it — embeds don’t execute in the Designer preview.
  • Squarespace: add a Code block where the wall should appear and paste the tag (leave the block set to HTML). Code blocks require a plan that allows custom code; if the block shows a script warning, save and view the live site — scripts don’t run in the editor.
  • WordPress: in the block editor, add a Custom HTML block and paste the tag. Classic editor: switch the editor tab from Visual to Text/Code first, or the tag will be escaped into visible text.
  • Framer: insert an Embed component (Insert → Utility → Embed), set it to HTML, and paste the tag. Set the component’s width to fill and give it enough height; check the published preview, since scripts don’t run on the canvas.

Related

  • API reference — the wall JSON shape and every endpoint.
  • Security — why the free-tier badge and the reporting link exist.
  • For AI agents — let an agent do the placement for you.