import type { Metadata } from 'next'; import Link from 'next/link'; import { HBars, StackedBars } from '@/components/charts/charts'; import { ChartBlock, Disclaimer, InlineRank, Note, StatGrid, TwoCol } from '@/components/stats/shared'; import { TypeBadge } from '@/components/ui/badges'; import { Container, PageHeader, Section, Stat } from '@/components/ui/section'; import { Unavailable } from '@/components/ui/unavailable'; import { api, safe } from '@/lib/api'; import { fmt2, fmtAgo, fmtDate, fmtInt, fmtKm, num } from '@/lib/format'; import { routes, SITE_NAME, SITE_URL } from '@/lib/site'; export const revalidate = 900; const TITLE = 'Space debris — catalogued fragments, rocket bodies and their sources'; const DESC = 'Tracked space debris on orbit: totals, debris by country, altitude distribution, fragmentation events with the most fragments still on orbit, debris growth by launch year and the largest tracked objects. Catalogued object counts only.'; export async function generateMetadata(): Promise { const url = `${SITE_URL}${routes.debris()}`; return { title: TITLE, description: DESC, alternates: { canonical: url }, openGraph: { title: `${TITLE} | ${SITE_NAME}`, description: DESC, url, type: 'website', siteName: SITE_NAME }, twitter: { card: 'summary_large_image', title: `${TITLE} | ${SITE_NAME}`, description: DESC }, }; } export default async function DebrisPage() { const res = await safe(api.debris()); const d = res?.data ?? null; const byCountry = d ? [...d.by_country].sort((a, b) => (num(b.debris) ?? 0) - (num(a.debris) ?? 0)) : []; const debrisBars = byCountry.filter((c) => (num(c.debris) ?? 0) > 0).slice(0, 12).map((c) => ({ label: c.name, value: num(c.debris) ?? 0, color: 'var(--series-4)' })); const rbBars = [...byCountry].sort((a, b) => (num(b.rocket_bodies) ?? 0) - (num(a.rocket_bodies) ?? 0)).filter((c) => (num(c.rocket_bodies) ?? 0) > 0).slice(0, 12).map((c) => ({ label: c.name, value: num(c.rocket_bodies) ?? 0, color: 'var(--series-2)' })); const byAlt = d ? [...d.by_altitude].sort((a, b) => (num(a.alt_km) ?? 0) - (num(b.alt_km) ?? 0)).map((r) => ({ x: String(num(r.alt_km) ?? 0), debris: num(r.debris) ?? 0, rocket_bodies: num(r.rocket_bodies) ?? 0 })) : []; const growth = d ? [...d.debris_growth].sort((a, b) => a.year - b.year).map((r) => { const cat = num(r.debris_catalogued) ?? 0; const still = num(r.debris_still_on_orbit) ?? 0; return { x: r.year, still_on_orbit: still, decayed: Math.max(0, cat - still) }; }) : []; const decays = d ? [...d.decays_by_year].sort((a, b) => a.year - b.year).map((r) => ({ x: r.year, payloads: num(r.payloads) ?? 0, debris: num(r.debris) ?? 0, rocket_bodies: num(r.rocket_bodies) ?? 0 })) : []; const largest = d ? [...d.largest_objects].sort((a, b) => (num(b.rcs_m2) ?? 0) - (num(a.rcs_m2) ?? 0)) : []; return ( {res &&

Generated {fmtAgo(res.meta.generated_at)}

}
{!d ? (
) : ( <>
{debrisBars.length ? : }
    {byCountry.slice(0, 12).map((c) => (
  • {c.name}
  • ))}
{rbBars.length ? : }
x-axis = lower edge of each 50 km perigee bin (km). Objects without a current element set are not placed. Informational density only.
{d.by_launch.length === 0 ? ( ) : ( {d.by_launch.map((l, i) => ( ))}
Launch Date Site Owners Debris on orbit
{l.primary_name ?? l.cospar_launch_id} {l.cospar_launch_id} {fmtDate(l.launch_date)} {l.site_name ?? '—'} {l.owner_codes?.join(', ') || '—'} {fmtInt(l.debris_on_orbit)}
)} Fragments are attributed to the COSPAR launch of their parent object. Owner codes are SATCAT designators (e.g. PRC, CIS, US).
Decayed = catalogued − still on orbit (derived from the two published series). Year = launch year of the parent object, not the fragmentation date. Published SATCAT decay dates. Recent reentries are listed on /reentries.
{largest.length === 0 ? ( ) : ( {largest.map((o, i) => ( ))}
Object Type RCS (m²) Perigee Apogee Launched Country
{o.name} {o.norad_id && #{o.norad_id}} {fmt2(o.rcs_m2)} {fmtKm(o.perigee_km)} {fmtKm(o.apogee_km)} {fmtDate(o.launch_date)} {o.country_code ?? '—'}
)} RCS = radar cross-section as published in the SATCAT (m²); it is a size proxy, not mass. {d.disclaimer}
)}
); }