Files
supabase/apps/www/components/Regions/RegionsExplorer.tsx
T
292c08b7b7 Added new /regions page (#49306)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature

## What is the current behavior?

Region and data residency information is split across docs, `/security`,
and legal pages.
[MARKET-1866](https://linear.app/supabase/issue/MARKET-1866/package-and-display-available-regions-better-on-website)

## What is the new behavior?

Adds `/regions`: a catalog of all 17 regions generated from
`regions.ts`, plus what stays in-region, the Europe vs EU caveat, and
links to the DPA, GDPR guide, sub-processor list, and security page.

Regions is in the footer under Security & Compliance. The security page
residency card now links here.

## Test plan

- [ ] Open `/regions` in light and dark mode
- [ ] Confirm the region count and list match
`packages/shared-data/regions.ts`
- [ ] Confirm footer Regions link and `/security` residency link go to
`/regions`


Made with [Cursor](https://cursor.com)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
  - Added a Regions page showcasing available AWS regions by geography.
- Added an interactive map and region list with selection, hover states,
keyboard accessibility, and residency badges.
- Included data residency guidance, legal resources, and a
call-to-action for next steps.
  - Added Regions links in the site footer and security documentation.

- **Documentation**
- Updated agent skill resources with expanded troubleshooting and
operational guidance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Francesco Sansalvadore <f.sansalvadore@gmail.com>
2026-08-27 12:45:28 +01:00

53 lines
1.8 KiB
TypeScript

'use client'
import dynamic from 'next/dynamic'
import { parseAsStringLiteral, useQueryState } from 'nuqs'
import { NuqsAdapter } from 'nuqs/adapters/next/pages'
import { useState } from 'react'
import { REGION_CODES } from './Regions.constants'
import { RegionsList } from './RegionsList'
const RegionsMap = dynamic(() => import('./RegionsMap').then((mod) => mod.RegionsMap), {
ssr: false,
loading: () => null,
})
// Unknown values are dropped, so `?region=nonsense` renders as no selection
const regionParser = parseAsStringLiteral(REGION_CODES).withOptions({ history: 'push' })
const RegionsExplorerContent = () => {
const [selectedRegionCode, setSelectedRegionCode] = useQueryState('region', regionParser)
const [hoveredRegionCode, setHoveredRegionCode] = useState<string>()
const handleRegionSelect = (code: string) =>
setSelectedRegionCode(code === selectedRegionCode ? null : code)
return (
<div className="flex flex-col gap-8 lg:gap-12">
{/* Matches the map's viewBox ratio, so the map fills the container instead of letterboxing */}
<div className="relative aspect-800/349 w-full overflow-hidden rounded-lg border bg-alternative">
<RegionsMap
selectedRegionCode={selectedRegionCode}
hoveredRegionCode={hoveredRegionCode}
onRegionHover={setHoveredRegionCode}
onRegionSelect={handleRegionSelect}
onRegionClear={() => setSelectedRegionCode(null)}
/>
</div>
<RegionsList
selectedRegionCode={selectedRegionCode}
hoveredRegionCode={hoveredRegionCode}
onRegionHover={setHoveredRegionCode}
onRegionSelect={handleRegionSelect}
/>
</div>
)
}
export const RegionsExplorer = () => (
<NuqsAdapter>
<RegionsExplorerContent />
</NuqsAdapter>
)