import type { Metadata } from "next"; import Link from "next/link"; import Header from "@/components/Header"; import Footer from "@/components/Footer"; import { createClient } from "@/lib/supabase/server"; import { createAdminClient } from "@/lib/supabase/admin"; import CompanyMentionsHover from "@/components/CompanyMentionsHover"; export const revalidate = 600; export const metadata: Metadata = { title: "2026 NAB Show — Live Coverage Hub | AV Beat", description: "AV Beat's live coverage of 2026 NAB Show: 1,100+ exhibitors, the latest product launches, booth-by-booth news, and editorial pick stories from the show floor.", alternates: { canonical: "/nab-2026" }, openGraph: { title: "2026 NAB Show — Live Coverage Hub", description: "Live coverage, exhibitor list, and breaking product news from 2026 NAB Show.", type: "website", url: "https://avbeat.com/nab-2026", }, }; type ExhibitorRow = { slug: string; company_name: string; logo_url: string | null; featured: boolean; mention_count: number; categories: string[] | null; }; type ArticleRow = { wp_slug: string; title: string; excerpt: string | null; featured_image: string | null; wp_published_at: string | null; }; export default async function NabHub() { const supabase = await createClient(); const { data: rawExhibitors } = await supabase .from("tracked_companies") .select("slug, company_name, logo_url, featured, mention_count, categories") .eq("directory_visible", true) .eq("exhibits_nab", true) .not("slug", "is", null) .order("featured", { ascending: false }) .order("mention_count", { ascending: false }) .order("company_name", { ascending: true }) .limit(2000); const exhibitors = (rawExhibitors || []) as ExhibitorRow[]; // Sponsor lookup so the hub can bubble paying advertisers to the top. let sponsors = new Set(); try { const adv = createAdminClient("adv"); const { data: ads } = await adv .from("active_advertisers_by_property") .select("company_name_lower") .eq("property", "broadcastbeat"); sponsors = new Set((ads || []).map((r: any) => String(r.company_name_lower))); } catch { /* tolerate */ } // Group exhibitors: sponsors first, then everything else alphabetically. const sponsorRows = exhibitors.filter((e) => sponsors.has(e.company_name.toLowerCase())); const otherRows = exhibitors .filter((e) => !sponsors.has(e.company_name.toLowerCase())) .sort((a, b) => a.company_name.localeCompare(b.company_name)); // Recent NAB stories — anything published in the last 60 days with NAB in // the title or tags. Cap at 30. const sixtyDaysAgo = new Date(Date.now() - 60 * 24 * 3600 * 1000).toISOString(); const { data: rawArticles } = await supabase .from("wp_imported_posts") .select("wp_slug, title, excerpt, featured_image, wp_published_at") .eq("status", "published") .or("title.ilike.%NAB%,title.ilike.%NAB 2026%") .gte("wp_published_at", sixtyDaysAgo) .order("wp_published_at", { ascending: false }) .limit(30); const articles = (rawArticles || []) as ArticleRow[]; return (
{/* Hero */}
Live coverage hub April 11–16, 2026 · Las Vegas

2026 NAB Show

{exhibitors.length.toLocaleString()} exhibitors confirmed. {sponsorRows.length > 0 && <> {sponsorRows.length} are AV Beat content partners.} {" "}Browse the directory, read the latest product news, and find every booth.

{/* Featured / Content Partners */} {sponsorRows.length > 0 && (

Content Partners at the 2026 NAB Show

{sponsorRows.length} exhibiting partner{sponsorRows.length === 1 ? "" : "s"}
{sponsorRows.map((e) => (
{e.logo_url ? ( /* eslint-disable-next-line @next/next/no-img-element */ {`${e.company_name} ) : ( {e.company_name.charAt(0).toUpperCase()} )}
{e.company_name} Sponsor ))}
)} {/* Recent NAB-related stories */} {articles.length > 0 && (

Latest from the show floor

See all NAB coverage →
{articles.slice(0, 9).map((a) => ( {a.featured_image && ( /* eslint-disable-next-line @next/next/no-img-element */ )}

{a.title}

{a.wp_published_at && (

{new Date(a.wp_published_at).toLocaleDateString("en-US", { month: "short", day: "numeric" })}

)}
))}
)} {/* Full exhibitor list */}

All 2026 NAB Show exhibitors

Full directory →
{otherRows.slice(0, 200).map((e) => ( {e.logo_url ? ( /* eslint-disable-next-line @next/next/no-img-element */ ) : (
{e.company_name.charAt(0).toUpperCase()}
)} {e.company_name} ))}
{otherRows.length > 200 && (

Showing 200 of {otherRows.length.toLocaleString()} exhibitors.{" "} Browse the full directory →

)}
); }