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

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

## 1. Простой HTML

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

```html
<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.

```tsx
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>` везде.

```js
// next.config.js
module.exports = {
  images: {
    remotePatterns: [
      { protocol: "https", hostname: "api.clearlogo.dev" },
    ],
  },
};
```

```tsx
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 слот, который остаётся визуально согласованным по брендам.

```tsx
<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.

```html
<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 компонент:

```tsx
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. Рендеринг списка с плейсхолдером

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

```tsx
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 не может найти логотип для домена, отрендерьте вместо этого детерминированный аватар с буквой.

```tsx
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 карточку.

```ts
// 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 был общедоступным (например, внутренние инструменты администратора), прокси через ваш бэкенд.

```ts
// 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 согласованным по всей базе кодов.

```ts
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 избыточности.

```html
<link rel="preconnect" href="https://api.clearlogo.dev" crossorigin />
<link rel="dns-prefetch" href="https://api.clearlogo.dev" />
```

```tsx
// 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 сделать остальное.
