
# アプリに企業ロゴをドロップする — コピーペーストレシピ

> ロゴが終わる一般的な形についての実践的なスニペット：`<img>`タグ、Reactコンポーネント、Next.js Image、Tailwind avatarスロット、ダークモード対応の`<picture>`要素、OGイメージフォールバック、およびサーバー署名URL。すべてのスニペットはパブリック`https://api.clearlogo.dev`エンドポイントに対して機能します。
>
> すべての例は、ブラウザキー（クライアントコード用）またはサーバーキー（バックエンドコード用）を持っていることを想定しています。[ダッシュボードからキーを作成します](https://clearlogo.dev/login?lang=ja)。

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

`next.config.js`にAPIホストを一度追加し、その後、最適化された`<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
    />
  );
}
```

HiDPIディスプレイで網膜の鮮鋭度のために`size * 2`をリクエストします。WebP交渉がAPIからクライアントにフローするようにしたい場合は`unoptimized`を使用します。Next.jsが形式交渉自体を処理するようにしたい場合は削除してください。

## 4. Tailwind avatarスロット

ロゴを固定された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やロゴごとの再レンダーはゼロです。

```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`をリクエストすることは常に安全です。アプリレベルのテーマスイッチ（OSレベルではない）の場合は、`prefers-color-scheme`の代わりにテーマ状態から宛先URLを駆動します。

## 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. 最初の文字付きエラーフォールバック

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

フォールバックはネットワークリクエストを実行しないため、たとえコールド行でもすばやくとどまります。

## 8. Open Graphイメージフォールバック

サーバー側フェッチを介して動的に生成された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 },
  );
}
```

ブラウザキーではなく**サーバー**キーをここで使用します — エッジ関数はサーバー側であり、サーバーキーは制限されていないオリジンヘッダーを通じて存続するものです。

## 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キーがなく、独自のCDNでキャッシング制御を実行します。

## 10. TypeScriptヘルパー

単一のヘルパーはコードベース全体でURL構築を一貫性保ちます。

```ts
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`はドメイン内の一般的でない文字に対して保護し、ヘルパーを信頼できない入力で呼び出すことを安全にします。

## 11. 接続を早期にウォームするためのプリコネクト

ダッシュボードが数十個のロゴをフォールド上方でレンダリングする場合、最初のリクエストはDNS、TCP、およびTLSをカバーします。単一の`<link rel="preconnect">`をドキュメントヘッドに追加して、最初の`<img>`がヒットする前に接続が準備できるようにします — 後続のすべてのロゴはURL単位でプリロードのブロートウェアなしで先手を取ります。

```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`はプリコネクトを無視するブラウザの安価なフォールバックです。2つのヒント、ページがレンダリングするロゴの数に関係なくです。ロゴごとの`<link rel="preload" as="image">`を優先してください — プリロードは重量級であり、帯域幅予算に対してカウントし、レンダリングされたURLが僅かに異なる場合は役立ちません（サイズ、テーマ、トークン）プリロードされたものから。

## FAQ

### どのレシピから始めるべきですか？

単一のロゴの場合、シンプルな`<img>`レシピで十分です。リストとテーブルの場合は、Tailwind avatarスロット + ロゴ遅延ロードパターンから始めます。

### テストのためにキーが必要ですか？

いいえ。エンドポイントは低ボリュームブラウジングのために匿名で機能します。ブラウザキーをプロダクションに配送する前に追加して、リクエストが適切に属され、プランに対してカウントされるようにします。

### どのサイズをリクエストする必要がありますか？

CSSでレンダリングされたサイズを一致させ、その後、視網膜の鮮鋭度のために`2x`をリクエストします。32ピクセルスロットのために`1024`をリクエストしないでください — 帯域幅を消費し、見た目が良くなっていません。

### JavaScriptなしでダークモードを処理する方法は？

`media="(prefers-color-scheme: dark)"`でゲート付きの`<source>`を持つ`<picture>`要素を使用します。ブラウザはペイント時に正しいURLを選択します — ReactフックなしでReactフック、ロゴごとのアサレンダーなし、長いリストでもロゴ単位のオーバーヘッドなし。

### ロゴのリストをより高速に読み込むにはどうすればよいですか？

単一の`<link rel="preconnect" href="https://api.clearlogo.dev">`をドキュメントヘッドに追加します。これにより、ページ上のすべてのロゴに対してDNS/TCP/TLSが一度開きます。これはURL単位での`<link rel="preload">`よりも安価です。

### APIがロゴを返さない場合、フォールバックを表示するにはどうすればよいですか？

`<img>`の`onError`をリッスンし、文字アバターまたはジェネリックアイコンにスワップします。上記のエラーフォールバックレシピはプロダクション形状です。

### サーバー側のレンダリングからブラウザキーを使用できますか？

できます — しかし、サーバーキーはより良いフィットです。なぜなら、オリジンチェックに依存するのではなく、`Authorization: Bearer …`を介してリクエストを送信するためです。Next.js Server Components、OGルート、またはバックエンドプロキシの場合は、サーバーキーを使用します。

### 独自のCDNでロゴをキャッシュするにはどうすればよいですか？

上記のバックエンド署名レシピを使用します。アップストリーム応答には長寿命`Cache-Control`が含まれるため、プロキシで同じヘッダーを設定でき、CDNに残りを処理させることができます。
