This is the official Ethereum.org website - a Next.js application that serves as the primary educational and community hub for Ethereum. The site is built with modern web technologies and focuses on accessibility, internationalization, and performance.
- Next.js 16+ - React framework with App Router (Turbopack is the default bundler; webpack available via
pnpm dev:webpack/pnpm build:webpack) - React 19 - UI library
- TypeScript 5.5+ - Type safety and development experience
- Tailwind CSS 4+ - Utility-first CSS framework (CSS-first config in
src/styles/global.css)
- next-intl 4+ - Internationalization (i18n) with 25 languages
- next-mdx-remote 5.0+ - MDX content processing
- Motion 12+ (
motionpackage, formerly Framer Motion) - Animations and transitions; import frommotion/react - Radix UI - Accessible component primitives
- shadcn/ui - Component library built on Radix UI
- Recharts - Data visualization
- Viem/Wagmi - Ethereum blockchain integration
- Storybook 10+ - Component development and testing
- Chromatic - Visual regression testing
- ESLint - Code linting with custom rules
- Prettier - Code formatting
- Husky - Git hooks
- PNPM - Package manager
- app/ - Next.js App Router pages
- [locale]/ - Internationalized routes
- src/
- components/ - React components
- ui/ - Design system components
- icons/ - SVG icon components
- data/ - Static data and configurations
- hooks/ - Custom React hooks
- i18n/ - Internationalization config
- intl/ - Translation files (25 languages)
- layouts/ - Page layout components
- lib/ - Utility functions and types
- constants.ts - App constants
- types.ts - TypeScript type definitions
- utils/ - Utility functions
- styles/ - Global styles and design tokens
- components/ - React components
- public/ - Static assets
- content/ - Markdown content files
- images/ - Image assets
- docs/ - Development documentation
- solutions/ - Documented solutions to past problems, organized by category with YAML frontmatter (module, tags, problem_type)
- Components: kebab-case (e.g.,
button-group.tsx) - Utilities: camelCase (e.g.,
cn.ts,relativePath.ts) - Pages: kebab-case following Next.js conventions
- Assets: kebab-case (e.g.,
eth-logo.png)
- Use
interfacefor object shapes,typefor unions/intersections - Prefer explicit typing over
any(ESLint enforcesfixToUnknown) - NEVER leave unused variables or parameters - ESLint
unused-imports/no-unused-varswill fail the Netlify build. The only allowed unused arg pattern is a single underscore_. Do NOT use_prefixedNames(e.g.,_foo) - either use the variable or remove it from the signature entirely. - Use generic constraints for reusable components
- Export types from dedicated files in
@/lib/types
- Primary approach: Tailwind CSS utility classes
- Component variants: Use
tailwind-variants(tv) for new and refactored work. Existingclass-variance-authority(cva) components don't need bulk migration -- swap totvopportunistically when you're already touching the component for another reason. - Dynamic classes: Use
cn()utility (clsx + tailwind-merge) - Custom properties: CSS variables in
src/styles/for theme values - Responsive design: Mobile-first approach
For UI work, see the design-system skill at .claude/skills/design-system/. It's the canonical knowledge base for component choices, design tokens, RTL/i18n, server/client boundaries, and the "use a variant, not a new component" pattern.
# Development
pnpm dev # Start development server (Turbopack)
pnpm dev:webpack # Start development server with webpack
pnpm build # Build for production (Turbopack)
pnpm build:webpack # Build for production with webpack
pnpm start # Start production server
# Code Quality
pnpm lint # Run ESLint
pnpm lint:fix # Fix ESLint issues
pnpm type-check # TypeScript type checking
pnpm format # Format with Prettier
# Testing
pnpm test:unit # Playwright unit tests (unit project)
pnpm test:e2e # Playwright end-to-end tests
pnpm test:visual # Playwright + Chromatic full-page visual tests
# Storybook
pnpm storybook # Start Storybook dev server
pnpm build-storybook # Build Storybook
pnpm chromatic # Run Chromatic visual tests
# Content Management
pnpm lint:md # Lint English markdown content
pnpm lint:md:fix # Auto-fix header IDs and duplicates- Unit Testing: Playwright
unitproject (pnpm test:unit) — heaviest coverage is the intl-pipeline sanitizer - E2E Testing: Playwright (
pnpm test:e2e), seedocs/e2e-testing.md - Visual Testing: Storybook + Chromatic for component regression; Playwright + Chromatic for full pages (see the
page-visual-testsskill) - Type Safety: TypeScript strict mode enabled
- Linting: ESLint with custom rules for imports and TypeScript
- 25 languages supported (canonical list:
i18n.config.json); RTL support for Arabic, Urdu - JSON UI strings in
src/intl/[locale]/; translated markdown content inpublic/content/translations/[locale]/ - Non-English markdown is propagated by the intl-pipeline (
src/scripts/intl-pipeline/, entrymain.ts). Do not hand-propagate English changes into non-English files -- let the pipeline run, or triggerintl-pipeline.ymlwithstamp_only: trueif manifests must catch up urgently (e.g. unblocking a build). Hand-fixing a translation error is fine when the English side hasn't moved, since the manifest mapping stays valid. Spec:tests/specs/PIPELINE-SPEC.md. - Glossary: base URL from
GLOSSARY_API_URLenv var; default insrc/scripts/intl-pipeline/config.ts. ETHGlossary is authoritative for Ethereum term translations.
For pipeline mechanics, recovery, manifests, ETHGlossary integration, and the intl/pending-{base} orchestration model, see the intl-pipeline skill at .claude/skills/intl-pipeline/. For translation-quality review (scoring rubric, language-group rules, ETHGlossary-as-authority policy, multi-agent role split), see the intl-review skill at .claude/skills/intl-review/.
- Educational content stored in
public/content/ - Processed with
next-mdx-remote - Custom MDX components for rich content
- Automatic table of contents generation
- All h1-h4 headings require a custom
{#lower-kebab-id}-- enforced by markdownlint via pre-commit hook. Runpnpm lint:md:fixto auto-add missing IDs. Config:.markdownlint-cli2.jsonc, custom rules:.markdownlint-rules/
- Images optimized with Next.js Image component
- SVGs loaded as React components via
@svgr/webpack - Static assets served from
public/ - Placeholder generation for images
- Sitemap generation in
app/sitemap.ts - Meta tags and Open Graph optimization
- Structured data for search engines
- Security headers (X-Frame-Options: DENY)
- Check existing patterns - Look at similar components first
- Prioritize Server Components - Use App Router and Server Components when possible
- Follow import order - ESLint will enforce, but be proactive
- Use TypeScript strictly - No
anytypes, preferunknown - Test in Storybook - Create stories for new components (filename pattern:
.stories.tsx) - Consider i18n - All user-facing text should be translatable. Server components:
getTranslationsandgetLocalefromnext-intl/server. Client components:useTranslationsfromnext-intl, one namespace-bound function per namespace - to access a second namespace, bind another function (e.g.const tCommon = useTranslations("common")) rather than reaching across namespaces. The legacy@/hooks/useTranslationwrapper (namespace:keysyntax) is deprecated for new code - Mobile-first - Design for mobile, enhance for desktop
- Accessibility - Use Radix primitives, semantic HTML
- Use locale-aware formatting wrappers - Use
numberFormat()fromsrc/lib/utils/numbers.tsinstead ofnew Intl.NumberFormat(), anddateTimeFormat()fromsrc/lib/utils/date.tsinstead ofnew Intl.DateTimeFormat()/.toLocaleDateString()/.toLocaleTimeString(). Both enforce correct numbering systems and calendar for Urdu and Arabic locales.
- Create component in appropriate
src/components/subdirectory- Use
src/components/uifor shadcn components or pure UI components
- Use
- Add TypeScript types and proper props interface
- Accept
refas a regular prop when needed (React 19);forwardRefonly survives in legacy components - Add Storybook story in same directory
- Export from appropriate index file
- Update documentation if adding new patterns
- Markdown files go in
public/content/ - Images in
public/images/with descriptive names - Translation strings in appropriate
src/intl/JSON files - Data files in
src/data/with TypeScript types
This project enforces type-safe chain names via TypeScript. When working with layer 2 networks or wallet data:
Critical Files:
src/data/chains.ts- Canonical source of all chain names (auto-updated weekly)src/lib/types.ts- DefinesChainNametype derived from chains.tssrc/data/networks/networks.ts- UseschainName: ChainNamesrc/data/wallets/wallet-data.ts- Usessupported_chains: ChainName[]
Rules:
- Always look up exact names - Before adding
chainNameorsupported_chains, searchchains.tsfor the exactnamevalue - Names are case-sensitive and exact - e.g., use
"Zircuit Mainnet"not"Zircuit", use"OP Mainnet"not"Optimism" - Run type checking - Use
pnpm type-checkto verify chain names are valid before committing - Non-EVM chains - For Starknet and other non-EVM chains, use
NonEVMChainNametype
Common Mistakes:
- Using informal names:
"Optimism"should be"OP Mainnet" - Missing "Mainnet" suffix:
"Zircuit"should be"Zircuit Mainnet" - Wrong casing:
"zksync Mainnet"should be"zkSync Mainnet"
@radix-ui/*- Accessible component primitivestailwind-variants- Component variant patternsmotion- Animation library (formerly framer-motion; import frommotion/react)lucide-react- Icon library
gray-matter- Frontmatter parsingrecharts- Data visualization
viem- Ethereum librarywagmi- React hooks for Ethereum@rainbow-me/rainbowkit- Wallet connection
The site uses a GDPR-compliant, cookie-less A/B testing system built on the Flags SDK precompute pattern, integrated with Matomo. The proxy assigns variants at the edge (header fingerprinting) and rewrites to signed, statically prerendered variant pages under ab-code/[code]/ — experiments never force dynamic rendering. Experiment status, weights, and scheduling live in the Matomo dashboard (no deploys needed). Only the default locale is tested; other locales always get the original.
Key gotcha: variants are matched by array index, not names — the variants array order must match the Matomo experiment order exactly, and the testKey must match the Matomo experiment name exactly.
Full guide (architecture, step-by-step recipe, env vars): docs/ab-testing.md. Code: proxy.ts, src/lib/ab-testing/, src/components/AB/, app/[locale]/ab-code/.
- Platform: Netlify (config in
netlify.toml) - Next.js Integration: Uses
@netlify/plugin-nextjsfor seamless Netlify and Next.js compatibility - Monitoring: Matomo analytics integration
The following external-looking services are managed by the ethereum.org team:
s3-dcl1.ethquokkaops.io— S3-compatible object storage for app screenshots and media. Used by the data layer to serve images for the/dappsand app listing pages. Downtime here means broken images on the live site.- Netlify Blobs (
@netlify/blobs) — Key-value store used by the data layer to cache API responses. Accessed viasrc/data-layer/storage.ts.