import type { MetroSummary } from "@dci/core"; import type { Metadata } from "next"; import Link from "next/link"; import { Breadcrumbs } from "@/components/entity/breadcrumbs"; import { EstimateMark } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Page, PageHeader } from "@/components/ui/card"; import { CountryFlag } from "@/components/ui/country-flag"; import { FilterChips, type Chip } from "@/components/ui/filter-chips"; import { Pagination } from "@/components/ui/pagination"; import { EmptyState } from "@/components/ui/states"; import { RowLink, TBody, THead, Table, Td, Th, Tr } from "@/components/ui/table"; import { api } from "@/lib/api"; import { fmtInt, fmtMw } from "@/lib/format"; import { routes } from "@/lib/routes"; import { pageMetadata } from "@/lib/seo"; import { compareBy, hrefWith, paginate, parseSort, sortTh, spGet, spInt, type SearchParams } from "@/lib/sort"; import { countryHref, countrySlugMap, pipelineMw } from "../country-links"; export const revalidate = 120; export const metadata: Metadata = pageMetadata({ title: "Data center markets", description: "Metro markets ranked by facilities, known capacity and pipeline — operators, cloud regions and internet exchanges per market.", path: routes.markets(), }); const SORT_KEYS = ["facilities", "mw", "pipeline", "operators", "cloud", "name"] as const; type SortKey = (typeof SORT_KEYS)[number]; const PER_PAGE = 50; const BASE = routes.markets(); const getter: Record number | string | null> = { facilities: (m) => m.facilityCount, mw: (m) => m.knownMw, pipeline: (m) => pipelineMw(m), operators: (m) => m.operatorCount, cloud: (m) => m.cloudRegionCount, name: (m) => m.name, }; export default async function MarketsPage({ searchParams }: { searchParams: Promise }) { const sp = await searchParams; const q = spGet(sp, "q"); const country = spGet(sp, "country")?.toUpperCase(); const sort = parseSort(sp, SORT_KEYS, { key: "facilities", order: "desc" }); const [res, { map, countries }] = await Promise.all([api.metros({ q, country }), countrySlugMap()]); const ok = res.items.length > 0; const sorted = [...res.items].sort((a, b) => compareBy(getter[sort.key], sort.order)(a, b) || a.name.localeCompare(b.name)); const { items, page } = paginate(sorted, spInt(sp, "page"), PER_PAGE); const th = (key: SortKey) => sortTh(BASE, sp, sort, key); const chips: Chip[] = []; if (q) chips.push({ key: "q", label: "Search", value: q, removeHref: hrefWith(BASE, sp, { q: undefined }) }); if (country) chips.push({ key: "country", label: "Country", value: countries.find((c) => c.iso2 === country)?.name ?? country, removeHref: hrefWith(BASE, sp, { country: undefined }) }); return ( Open map } />
{sort.key !== "facilities" && } {sort.key !== "facilities" && }
{items.length === 0 ? ( ) : ( {items.map((m) => { const pipe = pipelineMw(m); const cHref = countryHref(map, m.countryIso2); return ( ); })}
Market Country Facilities Op · Con · Plan Known MW Pipeline Operators Cloud regions IXPs Projects
{m.name}
{m.countryName ?? m.countryIso2} {m.regionName && m.regionName !== m.name ? ` · ${m.regionName}` : ""}
{cHref ? ( ) : ( )} {fmtInt(m.facilityCount)} {fmtInt(m.operationalCount)} · {fmtInt(m.constructionCount)} · {fmtInt(m.plannedCount)} {m.knownMw === null ? — : fmtMw(m.knownMw)} {pipe === null ? ( — ) : ( {fmtMw(pipe)} )} {fmtInt(m.operatorCount)} {m.cloudRegionCount ? fmtInt(m.cloudRegionCount) : —} {m.ixpCount ? fmtInt(m.ixpCount) : —} {m.projectCount ? fmtInt(m.projectCount) : —}
)}
hrefWith(BASE, sp, { page: p > 1 ? p : undefined })} label="markets" className="mt-3" />

Facilities are assigned to a market when they lie within its radius or their city matches one of its sub-markets; markets never span borders.{" "} Methodology

); }