← Blog
·8 min read

React Component for YouTube Channel Banners Using banner.yt

Build a reusable React component that displays a YouTube channel banner, avatar, and stats by fetching from the banner.yt API.

ReactComponentAPITutorialJavaScript
Blog Post Example Banner

YouTube Channel Banner Component in React

Here is a reusable React component that loads a channel banner, avatar, name, and subscriber count from the banner.yt API.

ChannelCard.tsx

import { useEffect, useState } from 'react';

interface ChannelData {
  channelId: string;
  title: string;
  description: string;
  subscriberCount: string;
  viewCount: string;
  bannerUrl: string;
  avatarUrl: string;
}

interface Props {
  channelId: string;
}

export default function ChannelCard({ channelId }: Props) {
  const [channel, setChannel] = useState<ChannelData | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(`https://banner.yt/api/channel/${channelId}`)
      .then(r => r.json())
      .then(data => {
        setChannel(data);
        setLoading(false);
      })
      .catch(() => setLoading(false));
  }, [channelId]);

  if (loading) return <div className="channel-card loading">Loading...</div>;
  if (!channel) return <div className="channel-card error">Channel not found</div>;

  const bannerSrc = `https://banner.yt/api/banner/${channelId}?format=webp`;
  const avatarSrc = `https://banner.yt/api/banner/${channelId}?type=avatar&format=webp`;

  const formatSubs = (n: string) => {
    const num = parseInt(n, 10);
    if (num >= 1e9) return (num / 1e9).toFixed(1) + 'B';
    if (num >= 1e6) return (num / 1e6).toFixed(1) + 'M';
    if (num >= 1e3) return (num / 1e3).toFixed(1) + 'K';
    return n;
  };

  return (
    <div style={{
      borderRadius: 12,
      overflow: 'hidden',
      border: '1px solid #333',
      background: '#111',
      maxWidth: 560,
    }}>
      <img
        src={bannerSrc}
        alt={`${channel.title} YouTube banner`}
        style={{ width: '100%', display: 'block' }}
        loading="lazy"
      />
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 12 }}>
        <img
          src={avatarSrc}
          alt={channel.title}
          width={48}
          height={48}
          style={{ borderRadius: '50%' }}
        />
        <div>
          <div style={{ fontWeight: 'bold', color: '#fff' }}>{channel.title}</div>
          <div style={{ fontSize: 13, color: '#aaa' }}>
            {formatSubs(channel.subscriberCount)} subscribers
          </div>
        </div>
      </div>
    </div>
  );
}

Usage

import ChannelCard from './ChannelCard';

export default function App() {
  return (
    <div>
      <h1>Top Channels</h1>
      <ChannelCard channelId="UCX6OQ3DkcsbYNE6H8uQQuVA" />
      <ChannelCard channelId="UC-lHJZR3Gqxm24_Vd_AJ5Yw" />
    </div>
  );
}

Next.js with server components

In Next.js 13+ you can fetch on the server and avoid the loading state entirely:

// app/channel/[id]/page.tsx
async function getChannel(id: string) {
  const res = await fetch(`https://banner.yt/api/channel/${id}`, {
    next: { revalidate: 3600 } // cache for 1 hour
  });
  return res.json();
}

export default async function ChannelPage({ params }: { params: { id: string } }) {
  const channel = await getChannel(params.id);

  return (
    <main>
      <img
        src={`https://banner.yt/api/banner/${params.id}`}
        alt={`${channel.title} banner`}
        style={{ width: '100%' }}
      />
      <h1>{channel.title}</h1>
      <p>{channel.subscriberCount} subscribers</p>
    </main>
  );
}