Tutorials
10 min read

Integrating a Company Logo API with Next.js 15 (App Router)

The complete guide to adding company logos to a Next.js 15 App Router app: server components, image optimization, caching, and the Next.js Image component configuration.

David Park

David Park

Frontend Engineer

Next.js 15 introduced significant changes to how images, caching, and server components work. This guide covers the idiomatic way to integrate a company logo API with the App Router — including the next.config.js changes, proper use of the Next.js Image component, and server-side caching patterns.

Initial Setup

First, add the LogoRouter domain to your next.config.ts so Next.js Image can optimize and proxy logos:

typescript
// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'img.logorouter.com',
        pathname: '/**',
      },
    ],
  },
};

export default nextConfig;

Using next/image with LogoRouter

The Image component handles sizing, format optimization, and lazy loading automatically:

tsx
// components/company-logo-next.tsx
import Image from 'next/image';

interface Props {
  domain: string;
  size?: number;
  className?: string;
}

export function CompanyLogoNext({ domain, size = 40, className }: Props) {
  return (
    <Image
      src={`https://img.logorouter.com/${domain}?size=${size * 2}&format=png`}
      alt={`${domain} logo`}
      width={size}
      height={size}
      className={className}
      unoptimized // LogoRouter already optimizes — skip double-optimization
    />
  );
}
Use unoptimized when the source is already a CDN-optimized image. Double-optimizing wastes your Next.js image optimization quota and adds latency.

Server Components: Fetching Data Alongside Logos

In a Server Component, you can fetch company data and resolve the logo URL in the same render pass:

tsx
// app/accounts/[domain]/page.tsx
import { Suspense } from 'react';
import Image from 'next/image';

interface PageProps {
  params: Promise<{ domain: string }>;
}

export default async function AccountPage({ params }: PageProps) {
  const { domain } = await params;

  // These run in parallel
  const [account, enrichment] = await Promise.all([
    getAccount(domain),
    fetch(`https://api.logorouter.com/v3/${domain}/enrichment`, {
      headers: { Authorization: `Bearer ${process.env.LOGOROUTER_API_KEY}` },
      next: { revalidate: 3600, tags: [`logo-${domain}`] }, // Next.js cache tags
    }).then(r => r.ok ? r.json() : null),
  ]);

  return (
    <main>
      <header className="flex items-center gap-4 p-6 border-b">
        <Image
          src={`https://img.logorouter.com/${domain}?size=128&format=webp`}
          alt={`${account.name} logo`}
          width={64}
          height={64}
          className="rounded-xl object-contain"
          unoptimized
          priority // Above the fold — don't lazy load
        />
        <div>
          <h1 className="text-xl font-semibold">{account.name}</h1>
          {enrichment && (
            <p className="text-sm text-muted-foreground">{enrichment.industry?.primary}</p>
          )}
        </div>
      </header>
    </main>
  );
}

Caching Strategy with Cache Tags

Use Next.js cache tags to invalidate specific company logos when they update:

typescript
// app/api/webhooks/logorouter/route.ts
import { revalidateTag } from 'next/cache';

export async function POST(req: Request) {
  const { type, data } = await req.json();

  if (type === 'logo.updated') {
    // Revalidate all pages that used this company's logo
    revalidateTag(`logo-${data.domain}`, 'max');
  }

  return Response.json({ ok: true });
}

Building a Logo Gallery with Streaming

For pages that show many logos (like a customer grid or partnership page), use Suspense streaming to progressively load:

tsx
// app/customers/page.tsx
import { Suspense } from 'react';

const CUSTOMERS = ['stripe.com', 'notion.so', 'linear.app', 'vercel.com', 'figma.com'];

function LogoGrid() {
  return (
    <div className="grid grid-cols-5 gap-8 items-center">
      {CUSTOMERS.map((domain) => (
        <div key={domain} className="flex justify-center">
          <Image
            src={`https://img.logorouter.com/${domain}?size=128&format=webp`}
            alt={`${domain} customer logo`}
            width={80}
            height={40}
            className="object-contain opacity-60 hover:opacity-100 transition-opacity"
            unoptimized
          />
        </div>
      ))}
    </div>
  );
}

export default function CustomersPage() {
  return (
    <section className="py-24">
      <h2 className="text-center text-sm text-muted-foreground uppercase tracking-widest mb-12">
        Trusted by teams at
      </h2>
      <Suspense fallback={<LogoGridSkeleton count={CUSTOMERS.length} />}>
        <LogoGrid />
      </Suspense>
    </section>
  );
}

Error Boundaries for Logo Failures

Wrap logo sections in error boundaries so a single broken logo does not crash your page:

tsx
// components/logo-error-boundary.tsx
'use client';

import { Component, type ReactNode } from 'react';

export class LogoErrorBoundary extends Component<
  { children: ReactNode; fallback?: ReactNode },
  { hasError: boolean }
> {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback ?? <div className="size-10 rounded-md bg-muted" />;
    }
    return this.props.children;
  }
}

Works perfectly with Next.js — zero configuration

LogoRouter is designed for modern Next.js apps. Start on the free tier and add your API key when you are ready to scale.

Community — free, no credit card
Get your API key
Start building today

Company logos and brand data, ready in 60 seconds

500,000 requests per month, completely free. No credit card. No contracts. Upgrade to a paid plan when you are ready to scale.

  • 500K requests / month free
  • 30M+ company logos
  • Sub-50ms global CDN
  • PNG, WebP & SVG formats
  • No credit card required

Topics covered

Tutorials
nextjs
app router
tutorial
next image
server components
caching