Next.js Server Components are perfect for displaying YouTube transcripts because they render on the server, keep your API key secure, and produce SEO-friendly HTML. In this tutorial, you will build a transcript viewer that takes a YouTube URL, fetches the transcript server-side, and displays it with clickable timestamps.
Project Setup
Create a new Next.js 14 project and add your API key to environment variables.
npx create-next-app@latest transcript-viewer --typescript --tailwind --app
cd transcript-viewer
# Add to .env.local
echo "YT_TRANSCRIPTS_API_KEY=your_key_here" >> .env.localCreate the Transcript Fetcher
Build a server-side utility function to fetch transcripts.
// lib/transcripts.ts
export interface TranscriptSegment {
text: string;
start: number;
duration: number;
}
export interface TranscriptData {
title: string;
channel: string;
duration: number;
transcript: TranscriptSegment[];
}
export async function getTranscript(videoUrl: string): Promise<TranscriptData> {
const res = await fetch(
`https://api.youtubetranscripts.co/v1/transcript?url=${encodeURIComponent(videoUrl)}`,
{
headers: { "x-api-key": process.env.YT_TRANSCRIPTS_API_KEY! },
next: { revalidate: 86400 },
}
);
if (!res.ok) {
throw new Error(`Failed to fetch transcript: ${res.status}`);
}
return res.json();
}Build the Page Component
Create a Server Component page that displays the transcript.
// app/transcript/[videoId]/page.tsx
import { getTranscript } from "@/lib/transcripts";
export default async function TranscriptPage({
params,
}: {
params: { videoId: string };
}) {
const data = await getTranscript(
`https://youtube.com/watch?v=${params.videoId}`
);
return (
<main className="mx-auto max-w-3xl px-4 py-12">
<h1 className="text-3xl font-bold">{data.title}</h1>
<p className="mt-2 text-gray-500">
{data.channel} · {Math.round(data.duration / 60)} min
</p>
<div className="mt-8 space-y-2">
{data.transcript.map((seg, i) => (
<div key={i} className="flex gap-4">
<a
href={`https://youtube.com/watch?v=${params.videoId}&t=${Math.floor(seg.start)}`}
className="shrink-0 text-sm text-blue-500 hover:underline"
target="_blank"
>
{formatTime(seg.start)}
</a>
<p>{seg.text}</p>
</div>
))}
</div>
</main>
);
}
function formatTime(seconds: number): string {
const m = Math.floor(seconds / 60);
const s = Math.floor(seconds % 60);
return `${m}:${s.toString().padStart(2, "0")}`;
}Add SEO Metadata
Generate dynamic metadata for each transcript page.
// Add to app/transcript/[videoId]/page.tsx
import type { Metadata } from "next";
import { getTranscript } from "@/lib/transcripts";
export async function generateMetadata({
params,
}: {
params: { videoId: string };
}): Promise<Metadata> {
const data = await getTranscript(
`https://youtube.com/watch?v=${params.videoId}`
);
return {
title: `${data.title} - Transcript`,
description: `Read the full transcript of "${data.title}" by ${data.channel}. ${data.transcript.length} segments, ${Math.round(data.duration / 60)} minutes.`,
};
}Create a Search Form
Add a client-side form for users to input YouTube URLs.
// app/page.tsx
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
export default function Home() {
const [url, setUrl] = useState("");
const router = useRouter();
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const match = url.match(/[?&]v=([a-zA-Z0-9_-]{11})/);
if (match) {
router.push(`/transcript/${match[1]}`);
}
}
return (
<main className="flex min-h-screen items-center justify-center">
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={url}
onChange={(e) => setUrl(e.target.value)}
placeholder="Paste YouTube URL..."
className="w-96 rounded border px-4 py-2"
/>
<button className="rounded bg-blue-600 px-6 py-2 text-white">
Get Transcript
</button>
</form>
</main>
);
}Conclusion
You have built a fully functional transcript viewer in Next.js with Server Components. The API key stays on the server, the transcript HTML is SEO-friendly, and the caching ensures fast page loads. Extend it with search, highlighting, and export features. Get your API key at youtubetranscripts.co.
Ready to start extracting YouTube transcripts?
Get 150 free API requests. No credit card required.
Get Your Free API Key