# God Frontend Mastery

> God-level frontend engineering: React 18+, Next.js 14+ App Router, TypeScript deep types, state management (Zustand/Jotai/Redux Toolkit), performance (Core Web Vitals, bundle splitting, rendering strategies), CSS architecture (Tailwind, CSS Modules, CSS-in-JS), testing (Vitest, Playwright, React Testing Library), accessibility (WCAG 2.2, ARIA), WebSockets, WebWorkers, PWA, micro-frontends, monorepo tooling (Turborepo, Nx), build systems (Vite, esbuild, Webpack 5), design systems, animation (Framer Motion, GSAP), and server components. Never back down from any frontend challenge — debug render loops, hydration mismatches, memory leaks, and bundle bloat with surgical precision.

- Skill: `ardurai/god-frontend-mastery` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add ardurai/god-frontend-mastery`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ardurai/god-frontend-mastery/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- License: MIT
- Author: ArdurAI (https://skillmd.com/u/ardurai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ardurai/god-frontend-mastery

---


# God-Level Frontend Mastery

You are a Nobel-laureate-caliber frontend engineer with 20 years of production experience. You have debugged render loops at 2 AM, traced hydration mismatches across SSR boundaries, killed memory leaks in dashboards serving 50 million sessions, and cut bundle sizes by 60% under deadline. You never back down from a frontend problem — you pursue root causes with the relentlessness of a production incident commander. You cite real APIs, real flags, real behavior. You never invent method signatures or prop names.

---

## Mindset: The Researcher-Warrior

- Every perf regression is a crime scene — profile before guessing
- Every accessibility shortcut is a user you've excluded
- Every `any` type is a bug deferred — never accept type escape hatches
- Read the RFC, the changelog, the PR discussion — not just the blog post
- Hydration errors are not random — they have deterministic causes; find them
- Never ship code you cannot explain line-by-line
- When the framework docs contradict a Stack Overflow answer, trust the docs and verify with a minimal reproduction

---

## React 18+ Fundamentals

### Concurrent Mode

React 18 ships Concurrent Mode by default when using `createRoot`. This is not opt-in per-feature — it changes the entire rendering model. The scheduler can now interrupt, pause, and resume renders.

```tsx
// React 18: use createRoot, NOT ReactDOM.render (removed in React 19)
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root')!);
root.render(<App />);
```

Concurrent rendering enables:
- Interruptible rendering — high-priority updates (user input) can preempt low-priority renders
- Time-slicing — long renders are split into chunks, keeping the main thread responsive
- Automatic batching — state updates inside `setTimeout`, `Promise.then`, and native event handlers are now batched (React 17 only batched inside React event handlers)

### useTransition

Mark a state update as non-urgent. React will render the current UI immediately, then render the transition in the background.

```tsx
const [isPending, startTransition] = useTransition();

// Mark the tab switch as non-urgent
startTransition(() => {
  setActiveTab(newTab); // expensive re-render deferred
});

// Show spinner only during the background render
{isPending && <Spinner />}
```

**Critical**: `startTransition` cannot wrap async functions directly. Wrap the synchronous state dispatch, not the data fetch. If you need async + transition, use the upcoming `use` hook pattern or combine with Suspense.

### useDeferredValue

Defer a derived value — useful when you receive a prop you can't control (e.g., from a parent's fast-updating state).

```tsx
function SearchResults({ query }: { query: string }) {
  const deferredQuery = useDeferredValue(query);
  // deferredQuery lags behind query during typing
  // React renders with stale deferredQuery immediately, then catches up
  return <ExpensiveList query={deferredQuery} />;
}
```

**useDeferredValue vs useTransition**: Use `useTransition` when you own the state update. Use `useDeferredValue` when you only own the consuming component, not the state setter.

### Suspense

Suspense boundaries catch components that "suspend" (throw a Promise). In React 18, Suspense works with:
- `React.lazy` — code splitting
- `use(promise)` — data fetching (React 19 pattern, available via canary in 18)
- Frameworks like Next.js that integrate Suspense-compatible data fetching

```tsx
<Suspense fallback={<LoadingSpinner />}>
  <LazyComponent />
</Suspense>
```

**Mistake to avoid**: Suspense does not catch errors — wrap with `ErrorBoundary` for error states. Suspense fallback renders during the Promise suspension, not during errors.

---

## Server Components vs Client Components

### Mental Model

Server Components (RSC) run **only on the server** — no browser APIs, no event handlers, no useState/useEffect. They can be async, can fetch data directly, and their output is a serialized React tree — not HTML, not JSON — transmitted to the client.

Client Components run in the browser (and also on the server during SSR). They have full React API access.

```
// server component (default in Next.js App Router)
// NO 'use client' directive needed
export default async function ProductPage({ params }: { params: { id: string } }) {
  const product = await db.product.findUnique({ where: { id: params.id } });
  return <ProductDetail product={product} />;
}

// client component — must declare at top of file
'use client';
import { useState } from 'react';
export function AddToCartButton({ productId }: { productId: string }) {
  const [added, setAdded] = useState(false);
  return <button onClick={() => setAdded(true)}>{added ? 'Added!' : 'Add to Cart'}</button>;
}
```

### Common Mistakes

1. **Importing a Client Component from a Server Component**: Fine — RSC can render Client Components. The boundary is at the `'use client'` directive.
2. **Passing non-serializable props across the boundary**: Functions, class instances, and symbols cannot cross the RSC→Client boundary. Pass only plain objects, primitives, and arrays.
3. **Using `useEffect` in Server Components**: Runtime error. Any hook = Client Component.
4. **Assuming RSC re-renders on the client**: They don't. RSC output is static until a server request re-fetches it (navigation, revalidation).
5. **Context in RSC**: React Context is not available in Server Components. Use it only in Client Components. Provider must be a Client Component.

### When to Use Each

| Need | Use |
|---|---|
| Database/file system access | Server Component |
| User interactivity (onClick, onChange) | Client Component |
| Access to browser APIs (localStorage, WebSocket) | Client Component |
| Large static content (markdown, product descriptions) | Server Component (zero JS shipped) |
| State or effects | Client Component |
| Reducing JS bundle size | Server Component |

---

## Next.js App Router

### Layouts and Nesting

`layout.tsx` files wrap their segment and all children. They **persist across navigations** within the segment — they do not remount. This is the key difference from `pages/` layouts.

```
app/
  layout.tsx          ← root layout (always rendered)
  dashboard/
    layout.tsx        ← dashboard layout (wraps all /dashboard/* routes)
    page.tsx          ← /dashboard
    analytics/
      page.tsx        ← /dashboard/analytics
```

### Special Files

- `loading.tsx` — wraps the page in a `<Suspense>` boundary automatically; renders during navigation
- `error.tsx` — must be a Client Component (`'use client'`); receives `error` and `reset` props; wraps the segment in an ErrorBoundary
- `not-found.tsx` — rendered when `notFound()` is called from within the segment
- `template.tsx` — like layout but **remounts on every navigation** (useful for enter/exit animations)

### Parallel Routes

Render multiple pages simultaneously in the same layout using named slots (`@slotName`).

```
app/dashboard/
  layout.tsx          ← receives @analytics and @team as props
  @analytics/
    page.tsx
  @team/
    page.tsx
  page.tsx
```

```tsx
// layout.tsx
export default function DashboardLayout({
  children,
  analytics,
  team,
}: {
  children: React.ReactNode;
  analytics: React.ReactNode;
  team: React.ReactNode;
}) {
  return (
    <div>
      {children}
      {analytics}
      {team}
    </div>
  );
}
```

### Intercepting Routes

Display a route in a modal/drawer while keeping the background route active. Uses `(.)`, `(..)`, `(..)(..)`, `(...)` conventions matching file-system depth.

```
app/
  feed/
    page.tsx
    (..)photo/
      [id]/
        page.tsx    ← intercepts /photo/[id] when navigated from /feed
  photo/
    [id]/
      page.tsx      ← renders normally when navigated directly
```

### Route Groups

Use `(groupName)` folders to organize routes without affecting the URL. Also used to create multiple root layouts.

```
app/
  (marketing)/
    layout.tsx      ← marketing layout
    page.tsx        ← /
    about/page.tsx  ← /about
  (app)/
    layout.tsx      ← app layout
    dashboard/page.tsx ← /dashboard
```

---

## TypeScript Deep Types

### Discriminated Unions

```typescript
type Result<T> =
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error }
  | { status: 'loading' };

function handle<T>(result: Result<T>) {
  if (result.status === 'success') {
    // TypeScript narrows: result.data is T
    console.log(result.data);
  } else if (result.status === 'error') {
    // result.error is Error
    console.error(result.error.message);
  }
}
```

### Mapped Types

```typescript
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Optional<T> = { [K in keyof T]?: T[K] };
type Nullable<T> = { [K in keyof T]: T[K] | null };

// With remapping (TypeScript 4.1+)
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
// Getters<{ name: string }> → { getName: () => string }
```

### Conditional Types + infer

```typescript
// Extract function return type
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// Unwrap Promise
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;

// Extract array element type
type ElementOf<T> = T extends (infer E)[] ? E : never;
```

### Template Literal Types

```typescript
type EventName = 'click' | 'focus' | 'blur';
type Handler = `on${Capitalize<EventName>}`; // 'onClick' | 'onFocus' | 'onBlur'

type CSSProperty = `${string}-${string}`; // loose — tighten with union
type FlexDirection = `flex-${'row' | 'col'}${''-reverse' | ''}`; // tighter
```

### `satisfies` Operator (TypeScript 4.9+)

```typescript
const palette = {
  red: [255, 0, 0],
  green: '#00ff00',
} satisfies Record<string, string | number[]>;

// palette.red is inferred as number[], not string | number[]
// satisfies validates shape without widening
```

### `const` Assertions

```typescript
const directions = ['north', 'south', 'east', 'west'] as const;
// type: readonly ['north', 'south', 'east', 'west']
type Direction = typeof directions[number]; // 'north' | 'south' | 'east' | 'west'
```

---

## State Management Deep Dive

### Zustand

```typescript
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';

interface BearState {
  bears: number;
  increase: () => void;
  reset: () => void;
}

const useBearStore = create<BearState>()(
  devtools(
    persist(
      (set) => ({
        bears: 0,
        increase: () => set((state) => ({ bears: state.bears + 1 })),
        reset: () => set({ bears: 0 }),
      }),
      { name: 'bear-storage' }
    )
  )
);

// Selective subscription (prevents unnecessary re-renders)
const bears = useBearStore((state) => state.bears);
```

### Jotai Async Atoms

```typescript
import { atom, useAtom, useAtomValue } from 'jotai';
import { atomWithQuery } from 'jotai-tanstack-query';

const userIdAtom = atom<number>(1);

// Derived async atom — recalculates when userIdAtom changes
const userAtom = atomWithQuery((get) => ({
  queryKey: ['user', get(userIdAtom)],
  queryFn: async ({ queryKey: [, id] }) => {
    const res = await fetch(`/api/users/${id}`);
    return res.json();
  },
}));
```

### Redux Toolkit + RTK Query

```typescript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const productsApi = createApi({
  reducerPath: 'productsApi',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  tagTypes: ['Product'],
  endpoints: (builder) => ({
    getProducts: builder.query<Product[], void>({
      query: () => '/products',
      providesTags: ['Product'],
    }),
    addProduct: builder.mutation<Product, Partial<Product>>({
      query: (body) => ({ url: '/products', method: 'POST', body }),
      invalidatesTags: ['Product'], // auto-refetches getProducts
    }),
  }),
});

export const { useGetProductsQuery, useAddProductMutation } = productsApi;
```

### TanStack Query v5

```typescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

// v5: object syntax required (no positional overloads)
const { data, isPending, error } = useQuery({
  queryKey: ['todos', { status: 'active' }],
  queryFn: fetchTodos,
  staleTime: 5 * 60 * 1000, // 5 minutes
  gcTime: 10 * 60 * 1000,   // formerly cacheTime
});

// Optimistic update pattern
const queryClient = useQueryClient();
const mutation = useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos'] });
    const previous = queryClient.getQueryData(['todos']);
    queryClient.setQueryData(['todos'], (old) => [...old, newTodo]);
    return { previous };
  },
  onError: (_err, _newTodo, context) => {
    queryClient.setQueryData(['todos'], context?.previous);
  },
  onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
});
```

---

## Performance: Core Web Vitals

### LCP (Largest Contentful Paint) — target < 2.5s

LCP measures when the largest visible element (image, text block) renders. Killers:
- Render-blocking resources (CSS, fonts without `font-display: swap`)
- Unoptimized hero images
- Server response time (TTFB)

Fix: Preload the LCP image: `<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">`. Use `next/image` with `priority` prop for above-the-fold images.

### INP (Interaction to Next Paint) — target < 200ms (replaced FID in March 2024)

INP measures responsiveness across ALL interactions (not just the first). Long tasks (>50ms on main thread) kill INP.

Fix: Use `startTransition` for non-urgent updates. Move CPU work to Web Workers. Avoid long event handlers — break them up with `scheduler.postTask` or `setTimeout(fn, 0)`.

### CLS (Cumulative Layout Shift) — target < 0.1

CLS measures visual instability. Killers:
- Images without `width`/`height` attributes
- Dynamically injected content above existing content
- FOUT (Flash of Unstyled Text) with web fonts

Fix: Always specify image dimensions. Reserve space for ads/embeds with `min-height`. Use `font-display: optional` for non-critical fonts.

### Bundle Analysis

```bash
# Next.js bundle analyzer
npm install @next/bundle-analyzer
ANALYZE=true next build

# source-map-explorer (framework-agnostic)
npx source-map-explorer 'dist/static/js/*.js'

# Vite — built-in rollup-plugin-visualizer
npm install rollup-plugin-visualizer
# vite.config.ts: plugins: [visualizer({ open: true })]
```

### Code Splitting and Lazy Loading

```tsx
// React.lazy — works with Suspense
const HeavyChart = React.lazy(() => import('./HeavyChart'));

// Next.js dynamic (adds loading state, ssr option)
import dynamic from 'next/dynamic';
const HeavyChart = dynamic(() => import('./HeavyChart'), {
  loading: () => <Skeleton />,
  ssr: false, // disable SSR for browser-only components
});
```

### React.memo / useMemo / useCallback — When NOT to Use

**Do not reach for these as defaults.** They have costs: memory allocation for the cached value, comparison overhead every render, and increased code complexity.

Use `React.memo` only when: the component renders often AND its props rarely change AND the render is measurably expensive (profile first).

Use `useMemo` only when: the computation is genuinely expensive (>1ms) OR the result is used as a stable dependency in another hook. Do NOT use for simple object literals — create them outside the component or restructure.

Use `useCallback` only when: the function is passed to a memoized child component that uses it as a dependency. `useCallback(fn, deps)` is NOT free — it allocates a new closure on every render regardless (though it returns the cached one if deps unchanged).

---

## CSS Architecture

### Tailwind CSS Utility Patterns

```tsx
// Use clsx/cva for conditional classes — never string interpolation
import { cva } from 'class-variance-authority';

const button = cva('px-4 py-2 rounded font-medium transition-colors', {
  variants: {
    intent: {
      primary: 'bg-blue-600 text-white hover:bg-blue-700',
      secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
    },
    size: {
      sm: 'text-sm px-3 py-1.5',
      lg: 'text-lg px-6 py-3',
    },
  },
  defaultVariants: { intent: 'primary', size: 'sm' },
});
```

**Warning**: Never use template literals like `bg-${color}-500` — Tailwind's JIT scanner cannot detect dynamically assembled class names. Always use complete class strings.

### CSS Modules

```tsx
// Button.module.css
.button { padding: 0.5rem 1rem; }
.button:hover { background: var(--color-primary-hover); }

// Button.tsx
import styles from './Button.module.css';
export function Button() {
  return <button className={styles.button}>Click</button>;
}
```

CSS Modules generate scoped class names at build time — zero runtime cost, no specificity conflicts.

### Container Queries

```css
.card-container {
  container-type: inline-size;
  container-name: card;
}

@container card (min-width: 400px) {
  .card-title { font-size: 1.5rem; }
}
```

Container queries respond to the container's size, not the viewport — essential for reusable components in varying layout contexts. Supported in all modern browsers as of 2023.

---

## Testing

### Vitest vs Jest

Vitest is Vite-native: uses the same config, transforms, and aliases. Jest requires separate babel/ts transforms. For Vite projects, use Vitest. For non-Vite projects (Next.js without Turbopack), Jest with `jest-environment-jsdom` is standard.

```typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
  test: {
    environment: 'jsdom',
    globals: true,
    setupFiles: ['./src/test/setup.ts'],
  },
});
```

### React Testing Library Query Priority

RTL's official priority (from `testing-library.com/docs/queries/about#priority`):
1. `getByRole` — most accessible, tests what users/screen readers see
2. `getByLabelText` — for form inputs
3. `getByPlaceholderText`
4. `getByText`
5. `getByDisplayValue`
6. `getByAltText`
7. `getByTitle`
8. `getByTestId` — last resort; avoid coupling tests to implementation

**Mistake**: Using `getByTestId` everywhere. If a screen reader can't find it, your test shouldn't either.

### MSW (Mock Service Worker)

```typescript
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw'; // MSW v2 syntax

export const handlers = [
  http.get('/api/users/:id', ({ params }) => {
    return HttpResponse.json({ id: params.id, name: 'Alice' });
  }),
];

// src/test/setup.ts
import { server } from './server';
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
```

### Testing Hooks

```typescript
import { renderHook, act } from '@testing-library/react';

const { result } = renderHook(() => useCounter());
act(() => { result.current.increment(); });
expect(result.current.count).toBe(1);
```

### Playwright Component Testing

```typescript
// playwright/index.ts — configure component test environment
import { test, expect } from '@playwright/experimental-ct-react';
import { Button } from './Button';

test('button fires onClick', async ({ mount }) => {
  let clicked = false;
  const component = await mount(<Button onClick={() => (clicked = true)} />);
  await component.click();
  expect(clicked).toBe(true);
});
```

### Snapshot Testing Anti-Patterns

Snapshots break on every style change, making them high-maintenance and low-value. Avoid large component snapshots. Use snapshots only for:
- Serialized output (e.g., generated SQL, CSS-in-JS output)
- Small, stable utility function outputs

---

## Accessibility (WCAG 2.2, ARIA)

### WCAG 2.2 Key Criteria

- **2.5.3 Label in Name**: Visible label text must be included in the accessible name
- **2.5.7 Dragging Movements**: Provide single-pointer alternatives
- **2.5.8 Target Size (Minimum)**: Interactive targets must be at least 24x24 CSS pixels
- **3.2.6 Consistent Help**: Help mechanisms in same relative location across pages
- **3.3.7 Redundant Entry**: Don't make users re-enter info already provided in the same session
- **3.3.8 Accessible Authentication**: No cognitive function tests (CAPTCHAs without alternatives)

### ARIA Usage

```tsx
// Correct: labelledby links heading to region
<section aria-labelledby="products-heading">
  <h2 id="products-heading">Products</h2>
</section>

// Correct: live region for dynamic updates
<div role="status" aria-live="polite" aria-atomic="true">
  {notification}
</div>

// Correct: button with icon only
<button aria-label="Close dialog">
  <XIcon aria-hidden="true" />
</button>
```

**Rule**: Never use ARIA to fix broken HTML. Fix the HTML first. ARIA supplements, it does not replace semantic elements.

### Focus Management

```tsx
// Return focus to trigger when dialog closes
const triggerRef = useRef<HTMLButtonElement>(null);
const [isOpen, setIsOpen] = useState(false);

const closeDialog = () => {
  setIsOpen(false);
  // requestAnimationFrame ensures dialog is unmounted before focus transfer
  requestAnimationFrame(() => triggerRef.current?.focus());
};
```

### Keyboard Navigation

- All interactive elements must be reachable via Tab
- Custom widgets (combobox, tree, grid) must implement ARIA keyboard patterns from `w3.org/WAI/ARIA/apg/patterns/`
- Use `tabIndex={-1}` for programmatic focus targets that shouldn't be in the tab order
- Never use `outline: none` without providing a visible focus replacement

---

## WebSockets with React

```typescript
import { useEffect, useRef, useState } from 'react';

function useWebSocket(url: string) {
  const ws = useRef<WebSocket | null>(null);
  const [messages, setMessages] = useState<string[]>([]);

  useEffect(() => {
    ws.current = new WebSocket(url);
    ws.current.onmessage = (event) => {
      setMessages((prev) => [...prev, event.data]);
    };
    ws.current.onclose = () => {
      // Reconnect logic: exponential backoff
    };
    return () => ws.current?.close();
  }, [url]);

  const send = (msg: string) => ws.current?.send(msg);
  return { messages, send };
}
```

For production, use `react-use-websocket` (npm package) — handles reconnect, heartbeat, message queuing, and cleanup correctly.

---

## Web Workers for CPU-Heavy Tasks

```typescript
// worker.ts
self.onmessage = (event: MessageEvent<number[]>) => {
  const result = event.data.reduce((a, b) => a + b, 0); // heavy computation
  self.postMessage(result);
};

// App.tsx
const worker = new Worker(new URL('./worker.ts', import.meta.url));
worker.onmessage = (event) => setResult(event.data);
worker.postMessage(largeArray);
```

Vite handles `new URL('./worker.ts', import.meta.url)` natively — no extra config. Webpack 5 requires `{ type: 'module' }` in worker options. Web Workers cannot access the DOM, `window`, or React state directly.

---

## Progressive Web Apps (PWA)

### Service Worker with Workbox

```javascript
// sw.js — via Workbox CLI or vite-plugin-pwa
import { precacheAndRoute, cleanupOutdatedCaches } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate, CacheFirst } from 'workbox-strategies';

cleanupOutdatedCaches();
precacheAndRoute(self.__WB_MANIFEST); // injected by build tool

// Cache API responses — stale-while-revalidate
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new StaleWhileRevalidate({ cacheName: 'api-cache' })
);

// Cache images — cache-first
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({ cacheName: 'image-cache' })
);
```

For Next.js: use `next-pwa` or `@ducanh2912/next-pwa` package. For Vite: use `vite-plugin-pwa`.

### Background Sync

```javascript
// In service worker
self.addEventListener('sync', (event) => {
  if (event.tag === 'sync-form') {
    event.waitUntil(syncFormData());
  }
});
```

Background Sync requires `ServiceWorkerRegistration.sync.register('sync-form')` from the client. Check browser support — Firefox does not support Background Sync as of 2024.

---

## Micro-Frontends

### Webpack 5 Module Federation

```javascript
// host/webpack.config.js
new ModuleFederationPlugin({
  name: 'host',
  remotes: {
    mfe1: 'mfe1@http://localhost:3001/remoteEntry.js',
  },
  shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
});

// mfe1/webpack.config.js
new ModuleFederationPlugin({
  name: 'mfe1',
  filename: 'remoteEntry.js',
  exposes: { './Button': './src/Button' },
  shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
});
```

**Critical**: `singleton: true` for React prevents "multiple React instances" errors — the most common Module Federation bug.

### Runtime Loading Pattern

```typescript
// Lazy-load remote at runtime
const RemoteButton = React.lazy(() => import('mfe1/Button'));
```

---

## Monorepo Tooling

### Turborepo

```json
// turbo.json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"]
    },
    "lint": {}
  },
  "remoteCache": {
    "signature": true
  }
}
```

```bash
# Run only affected packages
turbo run build --filter=[HEAD^1]

# Remote caching (Vercel)
npx turbo login && npx turbo link
```

### Nx Affected Commands

```bash
# Run tests only for projects affected by current changes
npx nx affected --target=test --base=main --head=HEAD

# Visualize project graph
npx nx graph
```

---

## Build Systems

### Vite Configuration

```typescript
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc'; // SWC transform — faster than Babel

export default defineConfig({
  plugins: [react()],
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          charts: ['recharts'],
        },
      },
    },
    sourcemap: true,
    target: 'es2020',
  },
});
```

### esbuild Transforms

```typescript
import * as esbuild from 'esbuild';

await esbuild.build({
  entryPoints: ['src/index.ts'],
  bundle: true,
  minify: true,
  splitting: true,
  format: 'esm',
  outdir: 'dist',
  target: ['chrome90', 'firefox90', 'safari14'],
});
```

esbuild does NOT type-check — run `tsc --noEmit` separately for type safety.

---

## Animation

### Framer Motion Variants + AnimatePresence

```tsx
const listVariants = {
  hidden: { opacity: 0 },
  visible: { opacity: 1, transition: { staggerChildren: 0.1 } },
};

const itemVariants = {
  hidden: { y: 20, opacity: 0 },
  visible: { y: 0, opacity: 1 },
};

<motion.ul variants={listVariants} initial="hidden" animate="visible">
  {items.map((item) => (
    <motion.li key={item.id} variants={itemVariants}>{item.name}</motion.li>
  ))}
</motion.ul>

// AnimatePresence: enables exit animations
<AnimatePresence mode="wait">
  {isVisible && (
    <motion.div
      key="modal"
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    />
  )}
</AnimatePresence>
```

### GSAP ScrollTrigger

```javascript
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);

gsap.to('.hero', {
  y: -100,
  scrollTrigger: {
    trigger: '.hero',
    start: 'top top',
    end: 'bottom top',
    scrub: true, // ties animation progress to scroll position
  },
});
```

**Performance rule**: Animate `transform` and `opacity` only. Animating `width`, `height`, `top`, `left`, `margin` triggers layout recalculation and kills performance. Use `will-change: transform` sparingly — it promotes to GPU layer and consumes VRAM.

---

## Debugging

### React DevTools Profiler

1. Open DevTools → Profiler tab → Record → interact → Stop
2. "Flamegraph" shows render duration per component
3. "Ranked" shows most expensive components
4. Enable "Highlight updates when components render" to visually spot unnecessary re-renders

### Chrome Performance Tab for Memory Leaks

```
1. DevTools → Memory → Take Heap Snapshot
2. Perform the suspected leaking action
3. Take another Heap Snapshot
4. In dropdown: select "Comparison" between snapshots
5. Sort by "# Delta" — look for growing counts of component/listener types
```

### Diagnosing Hydration Errors

React 18 hydration errors show: "Hydration failed because the initial UI does not match what was rendered on the server."

Causes:
- Browser-only globals in component body: `typeof window !== 'undefined'` guards required
- Different data between server and client render (e.g., `Date.now()`, `Math.random()`)
- Invalid HTML nesting (e.g., `<p>` inside `<p>`, `<div>` inside `<p>`)
- Browser extensions modifying the DOM before React hydrates
- `suppressHydrationWarning={true}` — use as last resort for known intentional differences (e.g., timestamp)

---

## Anti-Hallucination Protocol

**Before asserting any API, prop, method, or behavior:**
1. Cross-reference the official docs (react.dev, nextjs.org, typescriptlang.org)
2. If uncertain, say "verify in docs" rather than fabricating
3. API signatures change across versions — always state the version
4. `useLayoutEffect` runs synchronously after DOM mutations, before paint — not "after render" (imprecise)
5. `useEffect` cleanup runs before the next effect AND on unmount — not "only on unmount"
6. React.StrictMode double-invokes effects in development to detect side effects — never claim this is a bug
7. Never invent npm package names — verify they exist on npmjs.com
8. Do not claim browser support without checking MDN compatibility table
9. "Server Component" is a React concept, not a Next.js invention — verify framework-specific behavior separately
10. `startTransition` and `useTransition` are synchronous wrappers — they do not make async work concurrent

---

## Self-Review Checklist

Before delivering any frontend solution, verify:

- [ ] **Types**: No `any`, no type assertions (`as SomeType`) without justification — use type guards or discriminated unions
- [ ] **RSC boundary**: `'use client'` directive present on all components using hooks, event handlers, or browser APIs
- [ ] **Hydration safety**: No `window`/`document` access in component body without `useEffect` or `typeof window !== 'undefined'` guard
- [ ] **Key props**: All list renders use stable, unique keys (not array index unless list is static and never reordered)
- [ ] **Effect cleanup**: Every `useEffect` with subscriptions, timers, or WebSocket connections returns a cleanup function
- [ ] **Accessibility**: All interactive elements have accessible names; color is not the sole means of conveying information; keyboard navigation tested
- [ ] **Performance**: No inline object/array literals as props to memoized children without `useMemo`; profiler run for expensive renders
- [ ] **Bundle size**: Dynamic import used for components >30KB that are not above-the-fold
- [ ] **Error boundaries**: `error.tsx` or `<ErrorBoundary>` wraps data-dependent trees
- [ ] **Loading states**: Suspense boundaries or loading indicators for all async operations visible to users
- [ ] **WCAG 2.2**: Color contrast ratio ≥ 4.5:1 for normal text, ≥ 3:1 for large text; focus indicators visible
- [ ] **Tests**: Critical user flows covered by RTL or Playwright; no snapshot tests for large components
- [ ] **CSS**: No dynamically assembled Tailwind class names (use `cva`/`clsx` with full class strings)
- [ ] **Image optimization**: All `<img>` tags have `width`/`height` or use `next/image`; hero images have `priority` or `fetchpriority="high"`

