Integration cookbook
Drop a company logo into your app — copy-paste recipes
Production-ready snippets for adding company logos to your UI, from a plain img tag to React, Next.js Image, Tailwind avatars, dark-mode handling, OG fallbacks, and server-side signing.
Practical snippets for the common shapes a logo ends up in: an
<img>tag, a React component, a Next.js Image, a Tailwind avatar slot, a dark-mode-aware<picture>element, an OG image fallback, and a server-signed URL. Every snippet works against the publichttps://api.clearlogo.devendpoint.All examples assume you have a browser key (for client code) or a server key (for backend code). Create a key from the dashboard.
1. Plain HTML
Fastest possible integration — paste the URL into an <img> tag.
<img
src="https://api.clearlogo.dev/logo/github.com?size=64&content=80&token=YOUR_BROWSER_KEY"
alt="GitHub"
width="64"
height="64"
/>
Set size to match the rendered dimension so the browser doesn't waste bandwidth scaling a larger asset.
2. React component
A small reusable component covers most product UI needs.
type CompanyLogoProps = {
domain: string;
size?: 32 | 48 | 64 | 96 | 128;
alt?: string;
};
const BROWSER_KEY = process.env.NEXT_PUBLIC_CLEARLOGO_KEY!;
export function CompanyLogo({ domain, size = 64, alt }: CompanyLogoProps) {
return (
<img
src={`https://api.clearlogo.dev/logo/${domain}?size=${size}&content=80&token=${BROWSER_KEY}`}
alt={alt ?? `${domain} logo`}
width={size}
height={size}
loading="lazy"
decoding="async"
/>
);
}
loading="lazy" and decoding="async" keep long lists (CRMs, directories, account tables) from blocking paint.
3. Next.js <Image>
Add the API host to next.config.js once, then use the optimized <Image> component everywhere.
// next.config.js
module.exports = {
images: {
remotePatterns: [
{ protocol: "https", hostname: "api.clearlogo.dev" },
],
},
};
import Image from "next/image";
export function CompanyLogo({ domain, size = 64 }: { domain: string; size?: number }) {
return (
<Image
src={`https://api.clearlogo.dev/logo/${domain}?size=${size * 2}&content=80&token=${process.env.NEXT_PUBLIC_CLEARLOGO_KEY}`}
alt={`${domain} logo`}
width={size}
height={size}
unoptimized
/>
);
}
Request size * 2 for retina sharpness on HiDPI displays. Use unoptimized if you want the WebP negotiation from the API to flow through to the client; remove it if you want Next.js to handle format negotiation itself.
4. Tailwind avatar slot
Drop the logo into a fixed UI slot that stays visually consistent across brands.
<div className="flex items-center gap-3">
<div className="h-10 w-10 overflow-hidden rounded-md bg-neutral-100 ring-1 ring-neutral-200">
<img
src={`https://api.clearlogo.dev/logo/${domain}?size=64&content=80&token=${BROWSER_KEY}`}
alt=""
className="h-full w-full object-contain"
/>
</div>
<div>
<div className="font-medium">{name}</div>
<div className="text-sm text-neutral-500">{domain}</div>
</div>
</div>
object-contain plus a fixed slot size is the most reliable shape for tables and lists. Pair with the content=80 parameter so the logo never butts up against the slot edge.
5. Dark-mode-aware logo
Use <picture> with prefers-color-scheme to swap dark/light variants at paint time, with zero JavaScript or per-logo rerenders.
<picture>
<source
srcset="https://api.clearlogo.dev/logo/github.com?size=64&theme=dark&token=YOUR_BROWSER_KEY"
media="(prefers-color-scheme: dark)"
/>
<img
src="https://api.clearlogo.dev/logo/github.com?size=64&theme=light&token=YOUR_BROWSER_KEY"
alt="GitHub"
width="64"
height="64"
/>
</picture>
Wrapped as a React component:
export function CompanyLogo({ domain, size = 64 }: { domain: string; size?: number }) {
const base = `https://api.clearlogo.dev/logo/${domain}?size=${size}&content=80&token=${BROWSER_KEY}`;
return (
<picture>
<source srcSet={`${base}&theme=dark`} media="(prefers-color-scheme: dark)" />
<img src={`${base}&theme=light`} alt={`${domain} logo`} width={size} height={size} />
</picture>
);
}
The API falls back to the light variant when no dark version exists, so it's always safe to request theme=dark. For app-level theme switches (not OS-level), drive the source URL from your theme state instead of prefers-color-scheme.
6. List rendering with a placeholder
For long lists, render an immediate placeholder so empty slots don't flash.
function LogoCell({ domain }: { domain: string }) {
const [loaded, setLoaded] = useState(false);
return (
<div className="relative h-10 w-10 rounded-md bg-neutral-100">
<img
src={`https://api.clearlogo.dev/logo/${domain}?size=64&content=80&token=${BROWSER_KEY}`}
alt=""
loading="lazy"
onLoad={() => setLoaded(true)}
className={`h-full w-full object-contain transition-opacity ${
loaded ? "opacity-100" : "opacity-0"
}`}
/>
</div>
);
}
7. Error fallback with the first letter
If the API can't find a logo for a domain, render a deterministic letter avatar instead.
function LogoOrFallback({ domain, name }: { domain: string; name: string }) {
const [errored, setErrored] = useState(false);
if (errored) {
return (
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-neutral-200 font-medium text-neutral-600">
{name[0]?.toUpperCase() ?? "?"}
</div>
);
}
return (
<img
src={`https://api.clearlogo.dev/logo/${domain}?size=64&content=80&token=${BROWSER_KEY}`}
alt={`${name} logo`}
width={40}
height={40}
className="rounded-md"
onError={() => setErrored(true)}
/>
);
}
The fallback never network-requests, so even cold rows stay snappy.
8. Open Graph image fallback
Embed a logo into a dynamically-generated OG image via a server-side fetch.
// app/og/route.ts (Next.js, Edge runtime)
import { ImageResponse } from "next/og";
export const runtime = "edge";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const domain = searchParams.get("domain") ?? "example.com";
const logoUrl = `https://api.clearlogo.dev/logo/${domain}?size=256&content=80&token=YOUR_SERVER_KEY`;
return new ImageResponse(
(
<div style={{ display: "flex", alignItems: "center", padding: 64 }}>
<img src={logoUrl} width={128} height={128} alt="" />
<span style={{ marginLeft: 32, fontSize: 64 }}>{domain}</span>
</div>
),
{ width: 1200, height: 630 },
);
}
Use a server key here, not a browser key — Edge functions are server-side, and a server key is what survives unrestricted origin headers.
9. Backend signing for sensitive UIs
When you don't want the URL public (e.g. internal admin tools), proxy through your backend.
// app/api/logo/[domain]/route.ts (Next.js)
export async function GET(
request: Request,
{ params }: { params: { domain: string } },
) {
const upstream = await fetch(
`https://api.clearlogo.dev/logo/${params.domain}?size=128&content=80`,
{ headers: { Authorization: `Bearer ${process.env.CLEARLOGO_SERVER_KEY}` } },
);
return new Response(upstream.body, {
headers: {
"Content-Type": upstream.headers.get("Content-Type") ?? "image/png",
"Cache-Control": "public, max-age=86400, s-maxage=86400",
},
});
}
No API key in the page source, and you control caching at your own CDN.
10. TypeScript helper
A single helper keeps URL construction consistent across the codebase.
type LogoOptions = {
size?: 16 | 32 | 48 | 64 | 96 | 128 | 192 | 256 | 512 | 1024;
content?: number; // 50–100, step 5
theme?: "light" | "dark";
format?: "png" | "webp" | "jpeg";
};
export function logoUrl(domain: string, opts: LogoOptions = {}): string {
const params = new URLSearchParams({
size: String(opts.size ?? 64),
content: String(opts.content ?? 80),
token: process.env.NEXT_PUBLIC_CLEARLOGO_KEY!,
});
if (opts.theme) params.set("theme", opts.theme);
if (opts.format) params.set("format", opts.format);
return `https://api.clearlogo.dev/logo/${encodeURIComponent(domain)}?${params}`;
}
encodeURIComponent protects against unusual characters in domains and makes the helper safe to call with untrusted input.
11. Preconnect to warm the connection early
For dashboards that render dozens of logos above the fold, the first request pays for DNS, TCP, and TLS. Add a single <link rel="preconnect"> to your document head so the connection is ready before the first <img> hits — every subsequent logo gets a head start without per-URL preload bloat.
<link rel="preconnect" href="https://api.clearlogo.dev" crossorigin />
<link rel="dns-prefetch" href="https://api.clearlogo.dev" />
// Next.js
import Head from "next/head";
export function ClearLogoPreconnect() {
return (
<Head>
<link rel="preconnect" href="https://api.clearlogo.dev" crossOrigin="" />
<link rel="dns-prefetch" href="https://api.clearlogo.dev" />
</Head>
);
}
preconnect resolves DNS and opens the TCP/TLS handshake immediately; dns-prefetch is a cheap fallback for browsers that ignore the preconnect. Two hints total, regardless of how many logos the page renders. Prefer this over <link rel="preload" as="image"> per logo — preloads are heavyweight, count toward bandwidth budgets, and don't help if the rendered URL ends up slightly different (size, theme, token) from the preloaded one.
FAQ
Which recipe should I start with?
For a single logo, the plain <img> recipe is enough. For lists and tables, start with the Tailwind avatar slot + lazy-load pattern.
Do I need a key for testing?
No. The endpoint works anonymously for low-volume browsing. Add a browser key before you ship to production so the request is properly attributed and counted against your plan.
What size should I request?
Match the rendered size in CSS, then request 2x that for retina sharpness. Don't request 1024 for a 32-pixel slot — it costs bandwidth and doesn't look any better.
How do I handle dark mode without JavaScript?
Use a <picture> element with a <source> gated by media="(prefers-color-scheme: dark)". The browser picks the right URL at paint time — no React hook, no rerender per logo, no per-row overhead even in long lists.
How do I make a list of logos load faster?
Add a single <link rel="preconnect" href="https://api.clearlogo.dev"> in your document head. That opens DNS/TCP/TLS once for every logo on the page, which is cheaper than <link rel="preload"> per URL.
How do I show a fallback when the API returns no logo?
Listen for onError on the <img> and swap in a letter-avatar or a generic icon. The error fallback recipe above is the production shape.
Can I use a browser key from a server-side render?
You can — but a server key is a better fit because it sends the request via Authorization: Bearer … rather than relying on origin checks. For Next.js Server Components, OG routes, or backend proxies, use a server key.
How do I cache logos at my own CDN?
Use the backend-signing recipe above. The upstream response includes long-lived Cache-Control, so you can set the same headers on your proxy and let your CDN do the rest.
Try a real domain in the playground
Once the recipe shape looks right, swap in a few of your own customer or partner domains and preview the result before you ship.