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):
| Preset | What you get |
|---|---|
grid | Responsive multi-row grid (the default). |
row | Single static row, wraps on small screens. |
marquee | Continuously scrolling single lane, pauses on hover. |
two-row-marquee | Two staggered marquee lanes scrolling in opposite directions. |
mono-grid | Grid with a monochrome filter — logos in one tone. |
mono-marquee | Marquee with the same monochrome treatment. |
dark | Grid tuned for dark page backgrounds (light-variant logos where available). |
light | Grid tuned for light page backgrounds. |
compact | Tighter spacing and smaller logos — sidebars, footers. |
cards | Each logo in a bordered card tile. |
Attributes
| Attribute | Values | Default | Notes |
|---|---|---|---|
data-wall | wall public_id | required | demo works without an account. |
data-style | one of the 10 presets above | grid | Unknown values fall back to grid. |
data-display | logo · logo-name · name | logo | logo 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-height | number (px) | 40 | Logo height cap, clamped to 200. |
data-limit | number | 60 | Maximum 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.