Logos Marquee
Original · freelogo marquee (generic marks)
Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.
npx shadcn@latest add https://webinnoventix.com/r/mqx-logos.json"use client";
import { useId, useState, type CSSProperties, type ReactNode } from "react";
import { useReducedMotion } from "motion/react";
type Speed = "slow" | "normal" | "fast";
type Logo = { name: string; mark: ReactNode };
const SPEEDS: { id: Speed; label: string }[] = [
{ id: "slow", label: "Slow" },
{ id: "normal", label: "Normal" },
{ id: "fast", label: "Fast" },
];
const DURATION: Record<Speed, { a: number; b: number }> = {
slow: { a: 82, b: 98 },
normal: { a: 50, b: 62 },
fast: { a: 28, b: 36 },
};
const LOGOS: Logo[] = [
{
name: "Northwind",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" aria-hidden="true" focusable="false">
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="1.5" />
<path d="M12 6l2.2 3.8L18 12l-3.8 2.2L12 18l-2.2-3.8L6 12l3.8-2.2L12 6Z" fill="currentColor" />
</svg>
),
},
{
name: "Lumenire",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" aria-hidden="true" focusable="false">
<circle cx="12" cy="12" r="3.8" />
<path d="M12 2.5v3M12 18.5v3M2.5 12h3M18.5 12h3M5 5l2.1 2.1M16.9 16.9 19 19M19 5l-2.1 2.1M7.1 16.9 5 19" />
</svg>
),
},
{
name: "Vaultly",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M12 3 5 6v5c0 4.5 3 7.6 7 9 4-1.4 7-4.5 7-9V6l-7-3Z" />
<circle cx="12" cy="11" r="1.6" fill="currentColor" stroke="none" />
<path d="M12 12.6V15" strokeLinecap="round" />
</svg>
),
},
{
name: "Cascade",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="m5 6 7 5 7-5M5 11l7 5 7-5M5 16l7 5 7-5" />
</svg>
),
},
{
name: "Orbit Labs",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" aria-hidden="true" focusable="false">
<circle cx="12" cy="12" r="2.3" fill="currentColor" />
<ellipse cx="12" cy="12" rx="9" ry="3.9" stroke="currentColor" strokeWidth="1.5" />
<ellipse cx="12" cy="12" rx="9" ry="3.9" stroke="currentColor" strokeWidth="1.5" transform="rotate(60 12 12)" />
</svg>
),
},
{
name: "Halcyon",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M3 15c4 0 6.2-2.2 9-6 2.8 3.8 5 6 9 6" />
<path d="M12 9.2V18" opacity=".55" />
</svg>
),
},
{
name: "Quanta",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M12 3 20 7.5v9L12 21 4 16.5v-9L12 3Z" />
<circle cx="12" cy="12" r="2" fill="currentColor" stroke="none" />
</svg>
),
},
{
name: "Meridian",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden="true" focusable="false">
<circle cx="12" cy="12" r="9" />
<path d="M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18" />
</svg>
),
},
{
name: "Solstice",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" aria-hidden="true" focusable="false">
<path d="M4 16a8 8 0 0 1 16 0" />
<path d="M7 16a5 5 0 0 1 10 0" />
<circle cx="12" cy="16" r="1.4" fill="currentColor" stroke="none" />
</svg>
),
},
{
name: "Ironwood",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M12 3 6 12h3l-3 5h12l-3-5h3L12 3Z" />
<path d="M12 17v4" strokeLinecap="round" />
</svg>
),
},
{
name: "Nimbus",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M7 18a4 4 0 0 1 0-8 5 5 0 0 1 9.6-1.3A3.5 3.5 0 0 1 17 18H7Z" />
</svg>
),
},
{
name: "Fathom",
mark: (
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<circle cx="12" cy="4.6" r="1.8" />
<path d="M12 6.4V20M8.5 10h7M5 13a7 7 0 0 0 14 0M5 13h2M17 13h2" />
</svg>
),
},
];
function pillMarkup(logos: Logo[], keyPrefix: string): ReactNode {
return logos.map((logo) => (
<li
key={`${keyPrefix}-${logo.name}`}
className="mx-3 flex items-center gap-2.5 whitespace-nowrap rounded-xl border border-slate-200/80 bg-white/70 px-5 py-3 shadow-sm backdrop-blur-sm transition-colors duration-200 hover:border-indigo-300 hover:bg-white dark:border-slate-700/70 dark:bg-slate-900/50 dark:hover:border-indigo-500/60 dark:hover:bg-slate-900"
>
<span className="text-slate-400 dark:text-slate-500">{logo.mark}</span>
<span className="text-[15px] font-semibold tracking-tight text-slate-700 dark:text-slate-200">
{logo.name}
</span>
</li>
));
}
export default function MqxLogos() {
const uid = useId();
const reduce = useReducedMotion() ?? false;
const [isPlaying, setIsPlaying] = useState(true);
const [speed, setSpeed] = useState<Speed>("normal");
const { a: durA, b: durB } = DURATION[speed];
const rowB = [...LOGOS].reverse();
return (
<section className="relative w-full overflow-hidden bg-gradient-to-b from-white to-slate-50 px-6 py-20 dark:from-slate-950 dark:to-slate-900 sm:py-28">
<style>{`
@keyframes mqxlogos-marquee-a { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes mqxlogos-marquee-b { from { transform: translateX(-50%); } to { transform: translateX(0); } }
.mqxlogos-track {
animation-duration: var(--mqxlogos-dur, 50s);
animation-timing-function: linear;
animation-iteration-count: infinite;
will-change: transform;
}
.mqxlogos-track--a { animation-name: mqxlogos-marquee-a; }
.mqxlogos-track--b { animation-name: mqxlogos-marquee-b; }
.mqxlogos-viewport[data-playing="false"] .mqxlogos-track { animation-play-state: paused; }
.mqxlogos-viewport:hover .mqxlogos-track { animation-play-state: paused; }
.mqxlogos-viewport:focus-within .mqxlogos-track { animation-play-state: paused; }
.mqxlogos-fade {
-webkit-mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
}
@media (prefers-reduced-motion: reduce) {
.mqxlogos-track { animation: none !important; transform: none !important; }
}
`}</style>
<div className="mx-auto max-w-6xl">
<div className="flex flex-col items-start gap-8 sm:flex-row sm:items-end sm:justify-between">
<div className="max-w-xl">
<p className="text-xs font-semibold uppercase tracking-[0.22em] text-indigo-600 dark:text-indigo-400">
Our customers
</p>
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-slate-900 dark:text-white sm:text-4xl">
The companies building what’s next
</h2>
<p className="mt-4 text-base leading-relaxed text-slate-600 dark:text-slate-400">
From seed-stage startups to public enterprises, more than 40,000 teams
ship on our platform every week.
</p>
</div>
<div className="flex flex-wrap items-center gap-3">
<button
type="button"
onClick={() => setIsPlaying((v) => !v)}
aria-pressed={isPlaying}
aria-label={isPlaying ? "Pause logo animation" : "Play logo animation"}
className="inline-flex items-center gap-2 rounded-full border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 shadow-sm transition-colors hover:border-slate-400 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200 dark:hover:border-slate-600 dark:hover:bg-slate-800 dark:focus-visible:ring-offset-slate-950"
>
{isPlaying ? (
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true" focusable="false">
<rect x="6.5" y="5" width="3.5" height="14" rx="1" />
<rect x="14" y="5" width="3.5" height="14" rx="1" />
</svg>
) : (
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true" focusable="false">
<path d="M8 5.2v13.6a1 1 0 0 0 1.53.85l10.6-6.8a1 1 0 0 0 0-1.7L9.53 4.35A1 1 0 0 0 8 5.2Z" />
</svg>
)}
{isPlaying ? "Pause" : "Play"}
</button>
<fieldset className="m-0 border-0 p-0">
<legend className="sr-only">Marquee speed</legend>
<div className="inline-flex rounded-full border border-slate-300 bg-white p-1 shadow-sm dark:border-slate-700 dark:bg-slate-900">
{SPEEDS.map((s) => (
<label key={s.id} className="cursor-pointer">
<input
type="radio"
name={`${uid}-speed`}
value={s.id}
checked={speed === s.id}
onChange={() => setSpeed(s.id)}
className="sr-only peer"
/>
<span className="block rounded-full px-3.5 py-1.5 text-sm font-medium text-slate-600 transition-colors peer-checked:bg-indigo-600 peer-checked:text-white peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-indigo-500 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-white dark:text-slate-300 dark:peer-focus-visible:ring-offset-slate-950">
{s.label}
</span>
</label>
))}
</div>
</fieldset>
</div>
</div>
<ul className="sr-only">
{LOGOS.map((logo) => (
<li key={`sr-${logo.name}`}>{logo.name}</li>
))}
</ul>
{reduce ? (
<p className="mt-4 text-sm text-slate-500 dark:text-slate-500">
Motion is reduced in your system settings, so the logos are shown without
scrolling.
</p>
) : null}
<div
className="mqxlogos-viewport mqxlogos-fade mt-12 space-y-5"
data-playing={isPlaying ? "true" : "false"}
aria-hidden="true"
>
<div className="overflow-hidden">
<div
className="mqxlogos-track mqxlogos-track--a flex w-max"
style={{ ["--mqxlogos-dur"]: `${durA}s` } as CSSProperties}
>
<ul className="flex w-max shrink-0 items-center">{pillMarkup(LOGOS, "a1")}</ul>
<ul className="flex w-max shrink-0 items-center">{pillMarkup(LOGOS, "a2")}</ul>
</div>
</div>
<div className="overflow-hidden">
<div
className="mqxlogos-track mqxlogos-track--b flex w-max"
style={{ ["--mqxlogos-dur"]: `${durB}s` } as CSSProperties}
>
<ul className="flex w-max shrink-0 items-center">{pillMarkup(rowB, "b1")}</ul>
<ul className="flex w-max shrink-0 items-center">{pillMarkup(rowB, "b2")}</ul>
</div>
</div>
</div>
</div>
</section>
);
}Dependencies
Licence
Built by Web Innoventix. Free for personal and commercial use, no attribution required.
Built by Web Innoventix
Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.
Get a free quoteSimilar components
Browse all →
Glowing Logo Marquee
OriginalA dual-row infinite logo marquee whose rows scroll in opposite directions, pause on hover and lift each logo with a shimmer sweep over an animated gradient glow.

Vertical Testimonial Marquee
OriginalThree vertical testimonial columns scroll at different speeds and opposite directions, pausing on hover with a shine sweep so reviews loop endlessly beside a sticky heading.

Velocity Band Marquee
OriginalA skewed dual-direction headline marquee of big gradient and outlined words plus a pill ticker, with speed and pause controls and pause on hover over an animated mesh background.

Text Marquee
Originalscrolling text marquee

Cards Marquee
Originalcard marquee

Vertical Marquee
Originalvertical marquee columns

Gradient Fade Marquee
Originalmarquee with edge fade

Reverse Rows Marquee
Originaltwo rows scrolling opposite

Tilted Marquee
Originaltilted marquee band

Image Marquee
Originalimage marquee

Spotlight Hero
OriginalA centred hero with a soft radial spotlight, badge and dual call-to-action.

Split Hero
OriginalA two-column hero pairing a headline and CTAs with a product mock and social proof.

