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 14.2+ - React framework with App Router
- React 18 - UI library
- TypeScript 5.5+ - Type safety and development experience
- Tailwind CSS 3.4+ - Utility-first CSS framework
- next-intl 3.26+ - Internationalization (i18n) with 25 languages
- next-mdx-remote 5.0+ - MDX content processing
- Framer Motion 10.13+ - Animations and transitions
- Radix UI - Accessible component primitives
- shadcn/ui - Component library built on Radix UI
- Recharts - Data visualization
- Viem/Wagmi - Ethereum blockchain integration
- Storybook 8.6+ - 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
- Components: PascalCase (e.g.,
ActionCard.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
class-variance-authority(cva) - Dynamic classes: Use
cn()utility (clsx + tailwind-merge) - Custom properties: CSS variables in
:rootfor theme values - Responsive design: Mobile-first approach
# Development
pnpm dev # Start development server
pnpm build # Build for production
pnpm start # Start production server
# Code Quality
pnpm lint # Run ESLint
pnpm lint:fix # Fix ESLint issues
pnpm format # Format with Prettier
# Storybook
pnpm storybook # Start Storybook dev server
pnpm build-storybook # Build Storybook
pnpm chromatic # Run Chromatic visual tests
# Content Management
pnpm markdown-checker # Validate markdown content
pnpm events-import # Import community events- Visual Testing: Storybook + Chromatic for component regression
- Type Safety: TypeScript strict mode enabled
- Linting: ESLint with custom rules for imports and TypeScript
- Manual Testing: No automated unit tests - relies on type safety and visual testing
- 25 languages supported via Crowdin (canonical list:
i18n.config.json) - RTL support for Arabic, Urdu
- Translation files (JSON format) in
src/intl/[locale]/ - Content translations managed through Crowdin platform
- Educational content stored in
public/content/ - Processed with
next-mdx-remote - Custom MDX components for rich content
- Automatic table of contents generation
- 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 (use
getTranslationsandgetLocale) - 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
- Implement with proper forwardRef if needed
- 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
npx tsc --noEmitto 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 patternsframer-motion- Animation librarylucide-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 integrated with Matomo. Tests are configured entirely through the Matomo dashboard with no code changes required.
- Matomo API Integration - Experiments configured in Matomo dashboard
- Cookie-less Variant Persistence - Uses deterministic IP + User-Agent fingerprinting for variant assignment
- Server-side Rendering - No layout shifts, consistent variants on first load
- Real-time Updates - Change weights instantly via Matomo (no deployments)
- Preview Mode - Debug panel available in development and preview environments
- Automatic Fallbacks - Graceful degradation when API fails (shows original variant)
-
Create experiment in Matomo dashboard:
- Go to Experiments → Manage Experiments
- Create new experiment with desired name (e.g., "HomepageHero")
- Add variations with weights (original is implicit)
- Set status to "running"
-
Implement in component:
import ABTestWrapper from "@/components/AB/TestWrapper" ;<ABTestWrapper testKey="HomepageHero" // Must match Matomo experiment name exactly variants={[ <OriginalComponent key="current-hero" />, // Index 0: Original <NewComponent key="redesigned-hero" />, // Index 1: Variation ]} fallback={<OriginalComponent />} />
Important:
- Variants matched by array index, not names
- Array order must match Matomo experiment order exactly
- JSX
keyprops become debug panel labels:"redesigned-hero"→"Redesigned Hero" - No TypeScript changes required - system fetches configuration from Matomo
/api/ab-config- Fetches experiment data from Matomo APIsrc/lib/ab-testing/- Core logic for assignment and trackingsrc/components/AB/- React components for testing and debugging
Required for Matomo integration:
NEXT_PUBLIC_MATOMO_URL- Matomo instance URLNEXT_PUBLIC_MATOMO_SITE_ID- Site ID in MatomoMATOMO_API_TOKEN- API token with experiments access
- 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.