# Prerequisites: Node.js 20+ (below 22.0.0)
corepack enable && yarn set version berry
yarn && yarn dev
# Site available at http://localhost:3000/yarn build # Full build (prebuild + next build + postbuild)
yarn prebuild # Generate directory.json, flatDirectory.json, llms.txt
node src/directory/generateDirectory.mjs # Regenerate directory.json only
node src/directory/generateFlatDirectory.mjs # Regenerate flatDirectory.json onlyyarn test # Run all tests (Jest)
npx jest # Run all tests directly
npx jest <path> # Run specific test fileAll tests must pass before merging. Currently 259 tests across 60 suites.
yarn lint # ESLint + Next.js lint- Language: TypeScript, MDX
- Framework: Next.js 16 (Pages Router, static export with
output: 'export') - UI Library: @aws-amplify/ui-react
- Testing: Jest + @testing-library/react
- Styling: SCSS modules (imported via
src/styles/styles.scss) - Package Manager: Yarn Berry
- PascalCase for component directories and files:
GlobalNav/,MenuItem.tsx,CrossLink.tsx - camelCase for utility files:
findDirectoryNode.ts,getPageSection.ts,useCurrentPlatform.ts - kebab-case for page directories:
build-a-backend/,set-up-auth/,connect-to-API/ - SCSS files use kebab-case:
global-nav.scss,feedback.scss - Component directories have
index.tsbarrel exports:export { CrossLink } from './CrossLink'; - Tests live alongside source in
__tests__/directories
- All Gen2 pages live under
src/pages/[platform]/ - Backend pages:
src/pages/[platform]/build-a-backend/<feature>/ - Frontend pages:
src/pages/[platform]/frontend/<feature>/ - Gen1 pages:
src/pages/gen1/[platform]/— MUST NOT be modified - Every page is an MDX file (
index.mdx) that exportsmeta,getStaticPaths, andgetStaticProps
import { getCustomStaticPath } from '@/utils/getCustomStaticPath';
export const meta = {
title: 'Page Title',
description: 'Page description.',
platforms: [
'android', // Keep alphabetically sorted
'angular',
'flutter',
'javascript',
'nextjs',
'react',
'react-native',
'swift',
'vue'
]
};
export const getStaticPaths = async () => {
return getCustomStaticPath(meta.platforms);
};
export function getStaticProps(context) {
return {
props: {
platform: context.params.platform,
meta
}
};
}platformsarray MUST only include platforms where the content is relevantplatformsarray MUST be alphabetically sorted- Overview pages that show child cards use
getChildPageNodes(meta.route)ingetStaticProps
The docs use a section-based navigation system that filters content by category:
- Sections:
quickstart,backend,frontend,ui,hosting,reference - Section config:
src/data/sections.ts— labels, subtitles, URL helpers - Directory tree:
src/directory/directory.mjs— source of truth for navigation hierarchy - Section tags: Every node in
directory.mjshas asectionproperty ('backend','frontend','quickstart','hosting','ui','reference') - Filtering:
isNodeVisibleInSection()insections.ts— shared utility used by Menu, MenuItem, and Overview
| File | Purpose |
|---|---|
src/data/sections.ts |
Section definitions, getSectionFromPath(), getDefaultPathForSection(), isNodeVisibleInSection() |
src/utils/getPageSection.ts |
Directory tree walk for section detection + CrossLink feature route targeting |
src/directory/directory.mjs |
Hand-maintained navigation tree (source of truth) |
src/directory/generateDirectory.mjs |
Enriches directory.mjs with page metadata → writes directory.json |
src/components/GlobalNav/GlobalNav.tsx |
Top navigation bar with section tabs |
src/components/Menu/Menu.tsx |
Sidebar menu with section filtering |
src/components/Menu/MenuItem.tsx |
Recursive menu item with section-aware child filtering |
src/components/CrossLink/CrossLink.tsx |
Banner linking between backend ↔ frontend sections |
src/components/Overview/Overview.tsx |
Card grid for overview pages with section filtering |
- Pages showing
defineAuth,defineData,defineStorage, CDK config →section: 'backend' - Pages showing client API calls (
signIn,query,uploadData) →section: 'frontend' - Overview pages visible in multiple sections →
section: 'both'(only used if page appears in both backend and frontend sidebars) 'quickstart'tagged onhow-amplify-worksandstartsections (hidden from nav, shown on homepage)
This is the source of truth for all navigation. When adding or moving pages:
- Add/move the entry in
directory.mjswith the correctpathandsectiontag - Run
node src/directory/generateDirectory.mjsto regeneratedirectory.json - Run
node src/directory/generateFlatDirectory.mjsto regenerateflatDirectory.json
Structure:
export const directory = {
path: 'src/pages/index.tsx',
children: [
{
path: 'src/pages/[platform]/index.tsx',
children: [
{
path: 'src/pages/[platform]/build-a-backend/index.mdx',
section: 'backend',
children: [ /* auth, data, storage, functions, ai, services... */ ]
},
{
path: 'src/pages/[platform]/frontend/index.mdx',
section: 'frontend',
children: [ /* auth, data, storage, ai, analytics, geo... */ ]
},
// ... other sections
]
}
]
};When moving or deleting pages, add redirects to redirects.json:
{
"source": "/<platform>/old-path/",
"target": "/<platform>/new-path/",
"status": "301"
}- Use
<platform>placeholder (NOT[platform]) - Always include trailing slashes
- Use
<*>wildcard for path patterns:"/<platform>/old-prefix/<*>"→"/<platform>/new-prefix/<*>" - Place wildcard redirects BEFORE the catch-all
/<*>→/404/index.htmlentry - See Amplify Hosting redirect docs
Global MDX components are registered in mdx-components.tsx. Available in all MDX pages without import:
<InlineFilter filters={["react", "swift", ...]}>— Platform-conditional content<Callout>— Info/warning callouts<Accordion>— Expandable sections<BlockSwitcher>/<Block>— Tab-switchable code blocks<Overview childPageNodes={...} />— Card grid for child pages<CrossLink href="..." label="..." text="..." />— Cross-section link banner<Fragments>— Legacy fragment inclusion (prefer InlineFilter)
- Use
[link text](/[platform]/path/to/page/)format - Do NOT use relative links (
../page/) - The
[platform]placeholder resolves to the user's current platform - Backend pages:
/[platform]/build-a-backend/<feature>/<page>/ - Frontend pages:
/[platform]/frontend/<feature>/<page>/ - NEVER hardcode a specific platform in links (e.g.,
/react/...) — always use[platform]
- All styles in
src/styles/as SCSS files - Imported via
src/styles/styles.scss(global import order matters) - Use Amplify UI design tokens:
var(--amplify-colors-primary-80),var(--amplify-space-medium) - BEM-like naming:
.component,.component__element,.component__element--modifier - Dark mode via
@include darkMode { }mixin - Responsive breakpoints:
$mq-mobile: 1000px(desktop breakpoint)
- Functional components with TypeScript props
- Amplify UI primitives:
Flex,View,Text,Heading,Button,Badge,Card useRouter()for navigation,usePathWithoutHash()for current pathfindDirectoryNode()for directory tree lookupsgetPageSection()for section detection (client-side only — NEVER call during SSR/static generation)
This is a static export site (output: 'export'). Key rules:
- NEVER access
window,sessionStorage,localStorage, ordocumentoutside ofuseEffector event handlers - ALWAYS guard with
typeof window !== 'undefined'before browser API access - NEVER call
getPageSection()orfindDirectoryNode()during render — only inuseEffect useStateinitializers run on both server and client — must produce the same value to avoid hydration mismatch
- Gen1 pages at
/gen1/[platform]/MUST NOT be modified by section navigation changes - Gen1 pages show a legacy banner (
Gen1Bannercomponent) - Gen1 Docs link in sidebar navigates to
/gen1/<platform>/ - The
navbar--gen1class applies Gen1-specific nav styling
- Tests use Jest + @testing-library/react
- Router mocks required for component tests:
jest.mock('next/router', () => ({ __esModule: true, useRouter: () => ({ query: { platform: 'react' }, pathname: '/[platform]/build-a-backend', asPath: '/react/build-a-backend/' }) }));
- Test files:
src/<path>/__tests__/<Component>.test.tsx - Snapshot tests for stable UI components
- Accessibility tests use
axe-corevia CI pipeline
output: 'export'generates static HTML- Build output:
client/www/next-build/ - Amplify Hosting platform:
WEB(static), NOTWEB_COMPUTE(SSR) NODE_OPTIONS=--max-old-space-size=4096may be needed for large builds- Images optimized by
next-image-export-optimizerduring postbuild - Sitemap generated in
postBuildTasks.mjs
- No
getServerSideProps— static export only, usegetStaticProps+getStaticPaths - No Next.js middleware — not supported with
output: 'export' - No runtime redirects — all redirects are hosting-layer via
redirects.json - Trailing slashes required —
trailingSlash: trueinnext.config.mjs - TypeScript errors ignored in build —
ignoreBuildErrors: true(do not rely on build to catch type errors)