Назад

Рецепты интеграции

Поместите логотип компании в приложение — рецепты скопировать и вставить

Готовые к продакшену фрагменты для добавления логотипов компаний в ваш UI, от простого img тега к React, Next.js Image, Tailwind аватарам, обработке тёмного режима, OG fallbacks и подписыванию на стороне сервера.

Практические фрагменты для распространённых форм, в которые попадает логотип: тег <img>, компонент React, Next.js Image, слот аватара Tailwind, элемент <picture> с осведомлённостью о тёмном режиме, fallback для OG изображения и URL с подписью на стороне сервера. Каждый фрагмент работает с публичной конечной точкой https://api.clearlogo.dev.

Все примеры предполагают, что у вас есть ключ браузера (для кода клиента) или ключ сервера (для кода бэкенда). Создайте ключ из панели инструментов.

1. Простой HTML

Самая быстрая возможная интеграция — вставьте URL в тег <img>.

<img
  src="https://api.clearlogo.dev/logo/github.com?size=64&content=80&token=YOUR_BROWSER_KEY"
  alt="GitHub"
  width="64"
  height="64"
/>

Установите size так, чтобы он совпадал с визуализированным размером, чтобы браузер не тратил полосу пропускания на масштабирование большего актива.

2. React компонент

Небольшой повторно используемый компонент охватывает большинство потребностей продукт-UI.

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" и decoding="async" предотвращают блокирование больших списков (CRM, справочники, таблицы счётов) перед окраской.

3. Next.js <Image>

Добавьте хост API в next.config.js один раз, затем используйте оптимизированный компонент <Image> везде.

// 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
    />
  );
}

Запросите size * 2 для резкости retina на дисплеях HiDPI. Используйте unoptimized, если вы хотите, чтобы согласование WebP из API прошло к клиенту; удалите его, если вы хотите, чтобы Next.js сам обрабатывал согласование формата.

4. Слот аватара Tailwind

Поместите логотип в фиксированный UI слот, который остаётся визуально согласованным по брендам.

<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 плюс фиксированный размер слота это наиболее надёжная форма для таблиц и списков. Сочетайте с параметром content=80, чтобы логотип никогда не прижимался к краю слота.

5. Логотип с осведомлённостью о тёмном режиме

Используйте <picture> с источником prefers-color-scheme так, чтобы браузер поменял тёмный вариант при окраске — никакого JavaScript, никаких повторных рендеров и никаких сраб-хуков per-logo в списке из 50.

<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>

Завёрнуто как React компонент:

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>
  );
}

API возвращается к светлому варианту, когда нет тёмной версии, поэтому всегда безопасно запрашивать theme=dark. Для переключений темы уровня приложения (не уровня ОС), управляйте URL источника из состояния вашей темы вместо prefers-color-scheme.

6. Рендеринг списка с плейсхолдером

Для длинных списков отрендерьте немедленный плейсхолдер, чтобы пустые слоты не мерцали.

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. Fallback ошибки с первой буквой

Если API не может найти логотип для домена, отрендерьте вместо этого детерминированный аватар с буквой.

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)}
    />
  );
}

Fallback никогда не запрашивает сеть, поэтому даже холодные строки остаются бодрыми.

8. Fallback изображения Open Graph

Используйте fetch на стороне сервера, чтобы встроить логотип в вашу собственную динамически генерируемую OG карточку.

// 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 },
  );
}

Используйте ключ сервера здесь, а не ключ браузера — Edge функции это сторона сервера, и ключ сервера это то, что выживает при неограниченных заголовках origin.

9. Подписание на стороне бэкенда для чувствительных UI

Когда вы не хотите, чтобы URL был общедоступным (например, внутренние инструменты администратора), прокси через ваш бэкенд.

// 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",
    },
  });
}

Теперь ваш фронтенд использует /api/logo/{domain} — никакого ключа API в источнике страницы, и вы контролируете кэширование в вашем собственном CDN.

10. Помощник TypeScript

Единственный помощник сохраняет конструирование URL согласованным по всей базе кодов.

type LogoOptions = {
  size?: 16 | 32 | 48 | 64 | 96 | 128 | 192 | 256 | 512 | 1024;
  content?: number; // 50–100, шаг 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 защищает от необычных символов в доменах и делает помощник безопасным для вызова с ненадёжным вводом.

11. Preconnect для разогрева соединения ранее

Для панелей инструментов, которые рендерят дюжины логотипов выше края, первый запрос платит за DNS, TCP и TLS. Добавьте один <link rel="preconnect"> в head вашего документа так, чтобы соединение было готово перед первым хитом <img> — каждый последующий логотип получает фору без per-URL preload избыточности.

<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 решает DNS и открывает рукопожатие TCP/TLS мгновенно; dns-prefetch это дешёвый fallback для браузеров, которые игнорируют preconnect. Две подсказки в сумме, независимо от того, сколько логотипов рендерит страница. Предпочтите это <link rel="preload" as="image"> per логотипу — preloads это тяжелый вес, считаются при бюджетах пропускной способности и не помогают, если визуализированный URL оказывается немного другим (размер, тема, токен) от предварительно загруженного.

FAQ

С какого рецепта мне начать?

Если вы рендерите один логотип, простой рецепт <img> это достаточно. Для списков и таблиц, начните со слота аватара Tailwind и паттерном ленивой загрузки.

Нужен ли мне ключ для тестирования?

Нет. Конечная точка работает анонимно для маломощного просмотра. Добавьте ключ браузера перед отправкой в продакшен, так чтобы запрос был правильно отнесён на счёт и считается против вашего плана.

Какой размер мне запросить?

Совпадите с визуализированным размером в CSS, затем запросите 2x это для резкости retina. Не запрашивайте 1024 для слота в 32 пиксела — это стоит пропускной способности и не выглядит лучше.

Как мне обработать тёмный режим без JavaScript?

Используйте элемент <picture> с <source> защищённый media="(prefers-color-scheme: dark)". Браузер выбирает правильный URL при окраске — никакого React хука, никакого рендера per логотипу, никаких per-row накладных расходов даже в длинных списках.

Как мне сделать список логотипов быстрее загружающимся?

Добавьте один <link rel="preconnect" href="https://api.clearlogo.dev"> в head вашего документа. Это открывает DNS/TCP/TLS один раз для каждого логотипа на странице, что дешевле чем <link rel="preload"> per URL.

Как мне показать fallback, когда API возвращает без логотипа?

Слушайте onError на <img> и замените на аватар с буквой или общий значок. Рецепт fallback ошибки выше это продакшен форма.

Могу ли я использовать ключ браузера из server-side рендера?

Вы можете — но ключ сервера это лучше подходит, потому что отправляет запрос через Authorization: Bearer … вместо опирания на проверки origin. Для Next.js Server Components, OG маршрутов или бэкенд-прокси, используйте ключ сервера.

Как мне кэшировать логотипы на моём собственном CDN?

Используйте рецепт подписания на стороне бэкенда выше. Ответ апстрима включает долгоживущий Cache-Control, поэтому вы можете установить те же заголовки на вашем прокси и позволить вашему CDN сделать остальное.

Попробуйте реальный домен в playground

Как только форма рецепта выглядит правильно, подставьте несколько своих собственных доменов клиентов или партнёров и предпросмотрите результат перед отправкой.