1. Component Architecture Implementation Patterns

1.1 React Functional Components Hooks

Hook Syntax Description Use Case
useState const [state, setState] = useState(initial) Manages component local state with immutable updates Form inputs, toggles, counters
useEffect useEffect(() => {}, [deps]) Handles side effects, subscriptions, data fetching API calls, DOM manipulation, subscriptions
useContext const value = useContext(Context) Consumes React context without wrapper components Theme, auth, global state access
useReducer const [state, dispatch] = useReducer(reducer, init) Complex state logic with predictable state transitions Form validation, multi-step wizards
useMemo const value = useMemo(() => compute(), [deps]) Memoizes expensive computations between renders Large list filtering, complex calculations
useCallback const fn = useCallback(() => {}, [deps]) Memoizes function references to prevent re-renders Event handlers in optimized children
useRef const ref = useRef(initialValue) Persists mutable value without triggering re-renders DOM access, storing previous values
useLayoutEffect useLayoutEffect(() => {}, [deps]) Synchronous effect before browser paint DOM measurements, animations

Example: Complete functional component with hooks

import { useState, useEffect, useCallback, useMemo } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchUser = async () => {
      setLoading(true);
      const response = await fetch(`/api/users/${userId}`);
      const data = await response.json();
      setUser(data);
      setLoading(false);
    };
    fetchUser();
  }, [userId]);

  const handleUpdate = useCallback((updates) => {
    setUser(prev => ({ ...prev, ...updates }));
  }, []);

  const displayName = useMemo(() => 
    user ? `${user.firstName} ${user.lastName}` : 'Unknown',
    [user]
  );

  if (loading) return <div>Loading...</div>;

  return (
    <div>
      <h1>{displayName}</h1>
      <button onClick={() => handleUpdate({ active: true })}>
        Activate
      </button>
    </div>
  );
}

1.2 Vue 3 Composition API Setup

Function Syntax Description Use Case
ref const count = ref(0) Creates reactive primitive value with .value access Simple reactive state
reactive const state = reactive({}) Creates deeply reactive object proxy Complex nested state
computed const doubled = computed(() => count.value * 2) Cached derived state recalculated on dependency change Derived values, filters
watch watch(source, (newVal, oldVal) => {}) Performs side effects on reactive state changes API calls on input change
watchEffect watchEffect(() => console.log(count.value)) Automatically tracks dependencies and runs immediately Logging, analytics
onMounted onMounted(() => {}) Lifecycle hook after component DOM insertion Initialize third-party libraries
provide/inject provide('key', value); const val = inject('key') Dependency injection without prop drilling Plugin systems, themes

Example: Vue 3 Composition API component

<script setup>
import { ref, reactive, computed, watch, onMounted } from 'vue';

const count = ref(0);
const user = reactive({
  name: 'John',
  email: 'john@example.com'
});

const displayName = computed(() => 
  user.name.toUpperCase()
);

watch(count, (newCount, oldCount) => {
  console.log(`Count changed from ${oldCount} to ${newCount}`);
});

onMounted(() => {
  console.log('Component mounted');
});

const increment = () => count.value++;
</script>

<template>
  <div>
    <h1>{{ displayName }}</h1>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

1.3 Angular 17 Standalone Components NEW

Feature Syntax Description Use Case
@Component @Component({ standalone: true }) Self-contained component without NgModule Modern Angular architecture
imports imports: [CommonModule, FormsModule] Direct dependency declaration in component Explicit dependency management
Signal count = signal(0) Fine-grained reactive primitive for change detection Performance optimization
computed doubled = computed(() => count() * 2) Derived signal automatically updated Calculated values
effect effect(() => console.log(count())) Side effects triggered by signal changes Logging, analytics
Input/Output @Input() data; @Output() changed = new EventEmitter() Component communication interface Parent-child data flow

Example: Angular 17 standalone component with signals

import { Component, signal, computed } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-counter',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <div>
      <h2>Counter: {{ count() }}</h2>
      <p>Doubled: {{ doubled() }}</p>
      <button (click)="increment()">Increment</button>
      <button (click)="decrement()">Decrement</button>
    </div>
  `
})
export class CounterComponent {
  count = signal(0);
  doubled = computed(() => this.count() * 2);

  increment() {
    this.count.update(n => n + 1);
  }

  decrement() {
    this.count.update(n => n - 1);
  }
}

1.4 Atomic Design System Storybook

Level Component Type Description Examples
Atoms Basic building blocks Smallest functional units, not divisible Button, Input, Label, Icon
Molecules Simple groups Combinations of atoms functioning together SearchBar, FormField, Card
Organisms Complex sections Complex UI sections with distinct functionality Header, Footer, ProductCard
Templates Page layouts Page-level structure without real content HomePageLayout, DashboardLayout
Pages Instances Templates with real content and data HomePage, UserProfilePage

Example: Storybook configuration and component story

// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'Atoms/Button',
  component: Button,
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'danger']
    },
    size: {
      control: 'radio',
      options: ['sm', 'md', 'lg']
    }
  }
};

export default meta;
type Story = StoryObj<typeof Button>;

export const Primary: Story = {
  args: {
    variant: 'primary',
    children: 'Click Me',
    size: 'md'
  }
};

export const Secondary: Story = {
  args: {
    variant: 'secondary',
    children: 'Cancel',
    size: 'md'
  }
};

// SearchBar.stories.tsx (Molecule)
import { SearchBar } from './SearchBar';

export default {
  title: 'Molecules/SearchBar',
  component: SearchBar
};

export const Default = () => <SearchBar placeholder="Search..." />;
Note: Storybook v7+ uses CSF3 format with improved TypeScript support and automatic documentation generation.

1.5 Props Interface TypeScript Definitions

Pattern Syntax Description Use Case
Basic Interface interface Props { name: string } Type-safe prop definitions Component contracts
Optional Props title?: string Props that can be undefined Non-required attributes
Default Props name = 'Guest' Fallback values for props Default configurations
Union Types variant: 'primary' | 'secondary' Restricted string literal values Variant options
Generic Props interface Props<T> { data: T } Reusable type-safe components List, Table components
Children Prop children: React.ReactNode Type for child elements Container components
Event Handlers onClick: (e: MouseEvent) => void Type-safe event callbacks Interactive elements
Extending Props interface Props extends HTMLAttributes<T> Inherit native HTML props Wrapper components

Example: Comprehensive TypeScript component interfaces

// Basic Props Interface
interface ButtonProps {
  variant: 'primary' | 'secondary' | 'danger';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
  children: React.ReactNode;
}

function Button({ 
  variant, 
  size = 'md', 
  disabled = false, 
  onClick, 
  children 
}: ButtonProps) {
  return (
    <button 
      className={`btn btn-${variant} btn-${size}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

// Generic Props with Type Parameter
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
  keyExtractor: (item: T) => string | number;
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map(item => (
        <li key={keyExtractor(item)}>
          {renderItem(item)}
        </li>
      ))}
    </ul>
  );
}

// Extending HTML Attributes
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label: string;
  error?: string;
}

function Input({ label, error, ...props }: InputProps) {
  return (
    <div>
      <label>{label}</label>
      <input {...props} />
      {error && <span className="error">{error}</span>}
    </div>
  );
}

1.6 Component Composition Higher-Order Components

Pattern Description Use Case Example
HOC Pattern Function that takes component, returns enhanced component Cross-cutting concerns, auth withAuth(Component)
Render Props Component shares code via function prop Dynamic rendering logic <Toggle render={(on) => ...} />
Compound Components Multiple components work together as single unit Flexible APIs like Select/Option <Select><Option/></Select>
Container/Presentational Separate data logic from UI rendering Reusable UI components UserContainer + UserView
Custom Hooks Reusable stateful logic extraction Shared behavior across components useAuth(), useFetch()

Example: HOC for authentication and loading states

// Higher-Order Component Pattern
function withAuth<P extends object>(
  Component: React.ComponentType<P>
) {
  return function AuthenticatedComponent(props: P) {
    const { user, loading } = useAuth();

    if (loading) return <div>Loading...</div>;
    if (!user) return <Navigate to="/login" />;

    return <Component {...props} user={user} />;
  };
}

// Usage
const ProtectedDashboard = withAuth(Dashboard);

// Render Props Pattern
function DataFetcher({ url, render }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
  }, [url]);

  return render({ data, loading });
}

// Usage
<DataFetcher 
  url="/api/users" 
  render={({ data, loading }) => 
    loading ? <Spinner /> : <UserList users={data} />
  } 
/>

// Compound Components Pattern
const Select = ({ children, value, onChange }) => {
  return (
    <div className="select">
      {React.Children.map(children, child =>
        React.cloneElement(child, { selected: value, onSelect: onChange })
      )}
    </div>
  );
};

Select.Option = ({ value, selected, onSelect, children }) => (
  <div 
    className={selected === value ? 'selected' : ''}
    onClick={() => onSelect(value)}
  >
    {children}
  </div>
);

// Usage
<Select value={selected} onChange={setSelected}>
  <Select.Option value="1">Option 1</Select.Option>
  <Select.Option value="2">Option 2</Select.Option>
</Select>

Component Architecture Best Practices

  • Hooks preferred over class components in modern React for better composition
  • Use TypeScript interfaces for type-safe prop validation
  • Follow Atomic Design for consistent component hierarchy
  • Storybook essential for component development and documentation
  • Choose composition pattern based on reusability needs: HOC for cross-cutting, Render Props for dynamic logic, Compound for flexible APIs

2. Modern State Management Implementation

2.1 Redux Toolkit RTK Query Setup

Feature Syntax Description Use Case
configureStore configureStore({ reducer: {} }) Simplifies store setup with good defaults, DevTools Store initialization
createSlice createSlice({ name, initialState, reducers }) Generates action creators and reducers automatically State domain logic
createAsyncThunk createAsyncThunk('type', async () => {}) Handles async logic with pending/fulfilled/rejected actions API calls, side effects
createApi createApi({ baseQuery, endpoints }) RTK Query API service with auto-generated hooks Data fetching & caching
useSelector const data = useSelector(state => state.data) Extracts state with automatic re-render on changes Access store state
useDispatch const dispatch = useDispatch() Returns dispatch function to trigger actions Update state

Example: Redux Toolkit with RTK Query complete setup

// store.ts
import { configureStore } from '@reduxjs/toolkit';
import { setupListeners } from '@reduxjs/toolkit/query';
import { api } from './api';
import counterReducer from './counterSlice';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
    [api.reducerPath]: api.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(api.middleware),
});

setupListeners(store.dispatch);

// counterSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface CounterState {
  value: number;
  status: 'idle' | 'loading';
}

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0, status: 'idle' } as CounterState,
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
    incrementByAmount: (state, action: PayloadAction<number>) => {
      state.value += action.payload;
    },
  },
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;

// api.ts - RTK Query
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  tagTypes: ['User', 'Post'],
  endpoints: (builder) => ({
    getUsers: builder.query<User[], void>({
      query: () => '/users',
      providesTags: ['User'],
    }),
    getUserById: builder.query<User, string>({
      query: (id) => `/users/${id}`,
      providesTags: (result, error, id) => [{ type: 'User', id }],
    }),
    createUser: builder.mutation<User, Partial<User>>({
      query: (body) => ({
        url: '/users',
        method: 'POST',
        body,
      }),
      invalidatesTags: ['User'],
    }),
  }),
});

export const { useGetUsersQuery, useGetUserByIdQuery, useCreateUserMutation } = api;

// Component usage
function UserList() {
  const { data: users, isLoading, error } = useGetUsersQuery();
  const [createUser] = useCreateUserMutation();

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error loading users</div>;

  return (
    <div>
      {users?.map(user => <div key={user.id}>{user.name}</div>)}
      <button onClick={() => createUser({ name: 'New User' })}>
        Add User
      </button>
    </div>
  );
}

2.2 Zustand Lightweight Store Creation

Feature Syntax Description Use Case
create create((set) => ({ ...state })) Creates store with state and actions in single function Simple global state
set set((state) => ({ count: state.count + 1 })) Updates state immutably, triggers re-renders State mutations
get get().propertyName Reads current state without subscription Access state in actions
subscribe store.subscribe((state) => {}) Listen to state changes outside components Logging, persistence
persist persist(store, { name: 'key' }) Middleware for localStorage/sessionStorage persistence State persistence
immer immer((state) => { state.value++ }) Allows mutable syntax with Immer library Nested state updates

Example: Zustand store with middleware and actions

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

// Basic store
const useCountStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}));

// Advanced store with TypeScript, persist, and devtools
interface BearState {
  bears: number;
  fish: number;
  actions: {
    addBear: () => void;
    removeBear: () => void;
    addFish: (amount: number) => void;
  };
}

const useBearStore = create<BearState>()(
  devtools(
    persist(
      immer((set, get) => ({
        bears: 0,
        fish: 0,
        actions: {
          addBear: () => set((state) => { state.bears += 1; }),
          removeBear: () => set((state) => { state.bears -= 1; }),
          addFish: (amount) => set((state) => { 
            state.fish += amount; 
          }),
        },
      })),
      { name: 'bear-storage' }
    )
  )
);

// Separate selectors for optimization
const useBears = () => useBearStore((state) => state.bears);
const useActions = () => useBearStore((state) => state.actions);

// Component usage
function BearCounter() {
  const bears = useBears();
  const { addBear, removeBear } = useActions();

  return (
    <div>
      <h1>Bears: {bears}</h1>
      <button onClick={addBear}>Add Bear</button>
      <button onClick={removeBear}>Remove Bear</button>
    </div>
  );
}

// Async actions
const useUserStore = create((set, get) => ({
  user: null,
  loading: false,
  fetchUser: async (id) => {
    set({ loading: true });
    const response = await fetch(`/api/users/${id}`);
    const user = await response.json();
    set({ user, loading: false });
  },
}));

2.3 Context API Provider Pattern

Feature Syntax Description Use Case
createContext const Context = createContext(defaultValue) Creates context object for sharing data Global state initialization
Provider <Context.Provider value={data}> Makes context value available to descendants Wrapping component tree
useContext const value = useContext(Context) Consumes context value in child components Access shared state
Custom Hook Pattern const useAuth = () => useContext(AuthContext) Encapsulates context with validation and helpers Better DX, type safety
Multiple Contexts <Theme><Auth><App/></Auth></Theme> Compose multiple context providers Separation of concerns

Example: Context API with TypeScript and custom hook

// AuthContext.tsx
import { createContext, useContext, useState, ReactNode } from 'react';

interface User {
  id: string;
  name: string;
  email: string;
}

interface AuthContextType {
  user: User | null;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
  isAuthenticated: boolean;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

// Custom hook with validation
export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider');
  }
  return context;
}

// Provider component
export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);

  const login = async (email: string, password: string) => {
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
    });
    const userData = await response.json();
    setUser(userData);
  };

  const logout = () => {
    setUser(null);
  };

  const value = {
    user,
    login,
    logout,
    isAuthenticated: !!user,
  };

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

// Usage in component
function Profile() {
  const { user, logout, isAuthenticated } = useAuth();

  if (!isAuthenticated) {
    return <div>Please login</div>;
  }

  return (
    <div>
      <h1>Welcome, {user?.name}</h1>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

// App.tsx with provider
function App() {
  return (
    <AuthProvider>
      <Profile />
    </AuthProvider>
  );
}
Note: Context API re-renders all consumers when value changes. For performance-critical apps, split contexts or use useMemo on the value object.

2.4 Recoil Atom Selector Implementation

Feature Syntax Description Use Case
atom atom({ key: 'id', default: value }) Unit of state that components can subscribe to Shared state pieces
selector selector({ key: 'id', get: ({ get }) => {} }) Derived state from atoms or other selectors Computed values
useRecoilState const [val, setVal] = useRecoilState(atom) Read and write atom state (like useState) Read/write state
useRecoilValue const val = useRecoilValue(atom) Read-only access to atom/selector value Read-only state
useSetRecoilState const setVal = useSetRecoilState(atom) Write-only access without subscribing to changes Write-only operations
atomFamily atomFamily({ key: 'id', default: (param) => {} }) Creates dynamic atoms based on parameters List items, dynamic data
selectorFamily selectorFamily({ key: 'id', get: (param) => {} }) Creates parameterized selectors Filtered/transformed data

Example: Recoil atoms, selectors, and families

import { 
  atom, 
  selector, 
  atomFamily, 
  selectorFamily,
  useRecoilState, 
  useRecoilValue 
} from 'recoil';

// Basic atom
const textState = atom({
  key: 'textState',
  default: '',
});

// Selector for derived state
const charCountState = selector({
  key: 'charCountState',
  get: ({ get }) => {
    const text = get(textState);
    return text.length;
  },
});

// Async selector
const currentUserQuery = selector({
  key: 'currentUserQuery',
  get: async ({ get }) => {
    const userId = get(currentUserIdState);
    const response = await fetch(`/api/users/${userId}`);
    return response.json();
  },
});

// Atom family for dynamic state
const todoState = atomFamily({
  key: 'todoState',
  default: (id) => ({
    id,
    text: '',
    completed: false,
  }),
});

// Selector family for filtered data
const filteredTodosSelector = selectorFamily({
  key: 'filteredTodosSelector',
  get: (filter) => ({ get }) => {
    const todos = get(todoListState);
    switch (filter) {
      case 'completed':
        return todos.filter(todo => todo.completed);
      case 'active':
        return todos.filter(todo => !todo.completed);
      default:
        return todos;
    }
  },
});

// Component usage
function TextInput() {
  const [text, setText] = useRecoilState(textState);
  const charCount = useRecoilValue(charCountState);

  return (
    <div>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <p>Character Count: {charCount}</p>
    </div>
  );
}

// App with RecoilRoot
import { RecoilRoot } from 'recoil';

function App() {
  return (
    <RecoilRoot>
      <TextInput />
    </RecoilRoot>
  );
}

2.5 MobX Observable State Trees

Feature Syntax Description Use Case
makeObservable makeObservable(this, { prop: observable }) Makes class properties reactive Class-based stores
makeAutoObservable makeAutoObservable(this) Automatically makes all properties observable Simplified store setup
observable observable({ prop: value }) Creates observable object/array/map Object state
computed get computed() { return this.x * 2 } Derived values automatically recalculated Calculated properties
action @action method() { this.value = x } Functions that modify state State mutations
reaction reaction(() => data, (data) => {}) Side effects triggered by observable changes Logging, persistence
observer observer(Component) Makes React component reactive to observables Component observation

Example: MobX store with observer components

import { makeAutoObservable, runInAction } from 'mobx';
import { observer } from 'mobx-react-lite';

// Store class
class TodoStore {
  todos = [];
  filter = 'all';

  constructor() {
    makeAutoObservable(this);
  }

  // Computed property
  get filteredTodos() {
    switch (this.filter) {
      case 'active':
        return this.todos.filter(todo => !todo.completed);
      case 'completed':
        return this.todos.filter(todo => todo.completed);
      default:
        return this.todos;
    }
  }

  get completedCount() {
    return this.todos.filter(todo => todo.completed).length;
  }

  // Actions
  addTodo(text) {
    this.todos.push({
      id: Date.now(),
      text,
      completed: false,
    });
  }

  toggleTodo(id) {
    const todo = this.todos.find(t => t.id === id);
    if (todo) {
      todo.completed = !todo.completed;
    }
  }

  setFilter(filter) {
    this.filter = filter;
  }

  // Async action
  async fetchTodos() {
    const response = await fetch('/api/todos');
    const todos = await response.json();
    runInAction(() => {
      this.todos = todos;
    });
  }
}

// Create store instance
const todoStore = new TodoStore();

// Observer component - automatically re-renders on observable changes
const TodoList = observer(() => {
  const { filteredTodos, addTodo, toggleTodo } = todoStore;

  return (
    <div>
      <ul>
        {filteredTodos.map(todo => (
          <li key={todo.id}>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => toggleTodo(todo.id)}
            />
            {todo.text}
          </li>
        ))}
      </ul>
      <button onClick={() => addTodo('New Todo')}>Add</button>
    </div>
  );
});

// React Context for store
import { createContext, useContext } from 'react';

const StoreContext = createContext(todoStore);

export const useStore = () => useContext(StoreContext);

2.6 SWR Data Fetching Caching

Feature Syntax Description Use Case
useSWR const { data, error } = useSWR(key, fetcher) Hook for data fetching with built-in caching API data fetching
mutate mutate(key, data, options) Manually update cache and revalidate Optimistic updates
useSWRMutation const { trigger } = useSWRMutation(key, fetcher) Hook for mutations (POST, PUT, DELETE) Data modifications
SWRConfig <SWRConfig value={{ options }}> Global configuration provider Default settings
Revalidation { revalidateOnFocus: true } Auto-refetch on window focus, reconnect, interval Fresh data strategy
Pagination useSWRInfinite(getKey, fetcher) Infinite loading/pagination hook List pagination

Example: SWR with mutations and optimistic updates

import useSWR, { mutate, useSWRConfig } from 'swr';
import useSWRMutation from 'swr/mutation';

// Fetcher function
const fetcher = (url) => fetch(url).then(res => res.json());

// Basic usage
function UserProfile({ userId }) {
  const { data, error, isLoading, mutate } = useSWR(
    `/api/users/${userId}`,
    fetcher,
    {
      revalidateOnFocus: true,
      revalidateOnReconnect: true,
      dedupingInterval: 5000,
    }
  );

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <h1>{data.name}</h1>
      <button onClick={() => mutate()}>Refresh</button>
    </div>
  );
}

// Mutation with optimistic update
async function updateUser(url, { arg }) {
  await fetch(url, {
    method: 'PUT',
    body: JSON.stringify(arg),
  });
}

function EditProfile({ userId }) {
  const { trigger } = useSWRMutation(`/api/users/${userId}`, updateUser);

  const handleUpdate = async (updates) => {
    // Optimistic update
    mutate(
      `/api/users/${userId}`,
      (current) => ({ ...current, ...updates }),
      false // Don't revalidate yet
    );

    try {
      await trigger(updates);
    } catch (error) {
      // Revert on error
      mutate(`/api/users/${userId}`);
    }
  };

  return <button onClick={() => handleUpdate({ name: 'New Name' })}>Update</button>;
}

// Global configuration
import { SWRConfig } from 'swr';

function App() {
  return (
    <SWRConfig
      value={{
        refreshInterval: 30000,
        fetcher: (url) => fetch(url).then(res => res.json()),
        onError: (error) => console.error(error),
      }}
    >
      <UserProfile userId="123" />
    </SWRConfig>
  );
}

// Infinite loading
import useSWRInfinite from 'swr/infinite';

function UserList() {
  const getKey = (pageIndex, previousPageData) => {
    if (previousPageData && !previousPageData.hasMore) return null;
    return `/api/users?page=${pageIndex}&limit=10`;
  };

  const { data, size, setSize, isLoading } = useSWRInfinite(getKey, fetcher);

  const users = data ? data.flatMap(page => page.users) : [];
  const hasMore = data?.[data.length - 1]?.hasMore;

  return (
    <div>
      {users.map(user => <div key={user.id}>{user.name}</div>)}
      {hasMore && (
        <button onClick={() => setSize(size + 1)}>Load More</button>
      )}
    </div>
  );
}

State Management Comparison

Library Best For Learning Curve Bundle Size
Redux Toolkit Complex apps, DevTools, middleware Medium ~12KB
Zustand Simple API, minimal boilerplate Low ~1KB
Context API Small apps, theme/auth Low Built-in
Recoil Atomic state, derived values Medium ~21KB
MobX OOP style, automatic tracking Medium ~16KB
SWR Server state, caching, revalidation Low ~5KB

3. Data Flow Architecture Implementation

3.1 Flux Redux Unidirectional Flow

Concept Component Description Role
Action { type: 'ADD', payload: data } Plain object describing state change intent Event description
Dispatcher dispatch(action) Central hub distributing actions to stores Action distribution
Store function reducer(state, action) Holds application state, responds to actions State container
View useSelector(state => state.data) React component subscribing to store updates UI rendering
Middleware const logger = store => next => action => {} Intercepts actions for logging, async, etc Side effect handling

Example: Complete Flux/Redux unidirectional data flow

// Actions - describe what happened
const ActionTypes = {
  ADD_TODO: 'ADD_TODO',
  TOGGLE_TODO: 'TOGGLE_TODO',
  SET_FILTER: 'SET_FILTER',
};

const addTodo = (text) => ({
  type: ActionTypes.ADD_TODO,
  payload: { id: Date.now(), text, completed: false },
});

const toggleTodo = (id) => ({
  type: ActionTypes.TOGGLE_TODO,
  payload: { id },
});

// Reducer - specifies how state changes
function todosReducer(state = [], action) {
  switch (action.type) {
    case ActionTypes.ADD_TODO:
      return [...state, action.payload];
    
    case ActionTypes.TOGGLE_TODO:
      return state.map(todo =>
        todo.id === action.payload.id
          ? { ...todo, completed: !todo.completed }
          : todo
      );
    
    default:
      return state;
  }
}

// Store - holds state, provides dispatch
import { createStore, combineReducers } from 'redux';

const rootReducer = combineReducers({
  todos: todosReducer,
  filter: filterReducer,
});

const store = createStore(rootReducer);

// View - subscribes and dispatches
import { useSelector, useDispatch } from 'react-redux';

function TodoList() {
  const todos = useSelector(state => state.todos);
  const dispatch = useDispatch();

  return (
    <div>
      {todos.map(todo => (
        <div key={todo.id}>
          <input
            type="checkbox"
            checked={todo.completed}
            onChange={() => dispatch(toggleTodo(todo.id))}
          />
          {todo.text}
        </div>
      ))}
      <button onClick={() => dispatch(addTodo('New Todo'))}>
        Add Todo
      </button>
    </div>
  );
}

// Data Flow:
// 1. User clicks button → dispatch(action)
// 2. Action sent to reducer
// 3. Reducer creates new state
// 4. Store notifies subscribers
// 5. Component re-renders with new state
Note: Unidirectional flow ensures predictable state changes - state can only be modified through actions, making debugging easier with Redux DevTools.

3.2 GraphQL Apollo Client Normalization

Feature Syntax Description Use Case
ApolloClient new ApolloClient({ uri, cache }) GraphQL client with intelligent caching Client initialization
InMemoryCache new InMemoryCache({ typePolicies }) Normalized cache storing entities by ID Cache configuration
useQuery const { data, loading } = useQuery(QUERY) Fetches data with automatic caching and updates Data fetching
useMutation const [mutate] = useMutation(MUTATION) Executes mutations with cache updates Data modification
cache.writeQuery cache.writeQuery({ query, data }) Manually updates cache with new data Optimistic updates
refetchQueries { refetchQueries: [{ query: QUERY }] } Refreshes queries after mutation Cache invalidation
@client directive field @client Local-only fields not fetched from server Client-side state

Example: Apollo Client with normalized cache and optimistic updates

// Apollo Client setup
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';

const client = new ApolloClient({
  uri: 'https://api.example.com/graphql',
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          posts: {
            merge(existing = [], incoming) {
              return [...existing, ...incoming];
            },
          },
        },
      },
      User: {
        fields: {
          fullName: {
            read(_, { readField }) {
              return `${readField('firstName')} ${readField('lastName')}`;
            },
          },
        },
      },
    },
  }),
});

// GraphQL queries and mutations
const GET_USERS = gql`
  query GetUsers {
    users {
      id
      name
      email
    }
  }
`;

const CREATE_USER = gql`
  mutation CreateUser($input: UserInput!) {
    createUser(input: $input) {
      id
      name
      email
    }
  }
`;

// Component with query
import { useQuery, useMutation } from '@apollo/client';

function UserList() {
  const { data, loading, error } = useQuery(GET_USERS, {
    pollInterval: 30000, // Refetch every 30s
    fetchPolicy: 'cache-and-network',
  });

  const [createUser] = useMutation(CREATE_USER, {
    // Optimistic response
    optimisticResponse: {
      createUser: {
        __typename: 'User',
        id: 'temp-id',
        name: 'New User',
        email: 'temp@example.com',
      },
    },
    // Update cache after mutation
    update(cache, { data: { createUser } }) {
      const existing = cache.readQuery({ query: GET_USERS });
      cache.writeQuery({
        query: GET_USERS,
        data: {
          users: [...existing.users, createUser],
        },
      });
    },
  });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      {data.users.map(user => (
        <div key={user.id}>{user.name} - {user.email}</div>
      ))}
      <button onClick={() => createUser({ 
        variables: { input: { name: 'John', email: 'john@example.com' } } 
      })}>
        Add User
      </button>
    </div>
  );
}

// Cache normalization example
// Apollo automatically normalizes:
// { users: [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }] }
// Into:
// {
//   ROOT_QUERY: { users: [ref('User:1'), ref('User:2')] },
//   'User:1': { id: 1, name: 'John' },
//   'User:2': { id: 2, name: 'Jane' }
// }

3.3 React Query Optimistic Updates

Feature Syntax Description Use Case
useQuery useQuery({ queryKey, queryFn }) Declarative data fetching with caching Server state
useMutation useMutation({ mutationFn, onMutate }) Data mutations with rollback support Data updates
queryClient.setQueryData queryClient.setQueryData(key, data) Manually updates cached query data Optimistic updates
onMutate onMutate: async (newData) => {} Fires before mutation, returns rollback context Optimistic UI
onError onError: (err, vars, context) => {} Handles mutation errors, rolls back changes Error recovery
invalidateQueries queryClient.invalidateQueries(['key']) Marks queries as stale, triggers refetch Cache invalidation

Example: React Query with optimistic updates and rollback

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

function TodoList() {
  const queryClient = useQueryClient();

  // Fetch todos
  const { data: todos } = useQuery({
    queryKey: ['todos'],
    queryFn: async () => {
      const res = await fetch('/api/todos');
      return res.json();
    },
  });

  // Mutation with optimistic update
  const updateTodoMutation = useMutation({
    mutationFn: async (updatedTodo) => {
      const res = await fetch(`/api/todos/${updatedTodo.id}`, {
        method: 'PUT',
        body: JSON.stringify(updatedTodo),
      });
      return res.json();
    },
    // Optimistic update
    onMutate: async (updatedTodo) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({ queryKey: ['todos'] });

      // Snapshot current value
      const previousTodos = queryClient.getQueryData(['todos']);

      // Optimistically update cache
      queryClient.setQueryData(['todos'], (old) =>
        old.map((todo) =>
          todo.id === updatedTodo.id ? updatedTodo : todo
        )
      );

      // Return context with snapshot
      return { previousTodos };
    },
    // Rollback on error
    onError: (err, updatedTodo, context) => {
      queryClient.setQueryData(['todos'], context.previousTodos);
      console.error('Update failed, rolled back:', err);
    },
    // Refetch after success
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] });
    },
  });

  const deleteTodoMutation = useMutation({
    mutationFn: async (id) => {
      await fetch(`/api/todos/${id}`, { method: 'DELETE' });
    },
    onMutate: async (deletedId) => {
      await queryClient.cancelQueries({ queryKey: ['todos'] });
      const previousTodos = queryClient.getQueryData(['todos']);

      // Optimistically remove from UI
      queryClient.setQueryData(['todos'], (old) =>
        old.filter((todo) => todo.id !== deletedId)
      );

      return { previousTodos };
    },
    onError: (err, deletedId, context) => {
      queryClient.setQueryData(['todos'], context.previousTodos);
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] });
    },
  });

  const handleToggle = (todo) => {
    updateTodoMutation.mutate({
      ...todo,
      completed: !todo.completed,
    });
  };

  return (
    <div>
      {todos?.map((todo) => (
        <div key={todo.id}>
          <input
            type="checkbox"
            checked={todo.completed}
            onChange={() => handleToggle(todo)}
          />
          {todo.text}
          <button onClick={() => deleteTodoMutation.mutate(todo.id)}>
            Delete
          </button>
        </div>
      ))}
    </div>
  );
}

3.4 WebSocket Real-time Data Sync

Feature API/Method Description Use Case
WebSocket new WebSocket('ws://url') Creates persistent bidirectional connection Real-time communication
onopen ws.onopen = () => {} Fires when connection established Connection ready
onmessage ws.onmessage = (event) => {} Receives messages from server Incoming data
send ws.send(JSON.stringify(data)) Sends data to server Outgoing messages
onerror ws.onerror = (error) => {} Handles connection errors Error handling
onclose ws.onclose = () => {} Fires when connection closes Cleanup, reconnection
Socket.io io('http://url') Library with auto-reconnection, rooms, namespaces Enhanced WebSocket

Example: WebSocket with React hook and reconnection logic

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

// Custom WebSocket hook
function useWebSocket(url) {
  const [messages, setMessages] = useState([]);
  const [connectionStatus, setConnectionStatus] = useState('disconnected');
  const ws = useRef(null);
  const reconnectTimeoutRef = useRef(null);

  const connect = () => {
    ws.current = new WebSocket(url);

    ws.current.onopen = () => {
      console.log('Connected');
      setConnectionStatus('connected');
    };

    ws.current.onmessage = (event) => {
      const data = JSON.parse(event.data);
      setMessages((prev) => [...prev, data]);
    };

    ws.current.onerror = (error) => {
      console.error('WebSocket error:', error);
      setConnectionStatus('error');
    };

    ws.current.onclose = () => {
      console.log('Disconnected');
      setConnectionStatus('disconnected');
      
      // Auto-reconnect after 3 seconds
      reconnectTimeoutRef.current = setTimeout(() => {
        console.log('Reconnecting...');
        connect();
      }, 3000);
    };
  };

  useEffect(() => {
    connect();

    return () => {
      if (reconnectTimeoutRef.current) {
        clearTimeout(reconnectTimeoutRef.current);
      }
      if (ws.current) {
        ws.current.close();
      }
    };
  }, [url]);

  const sendMessage = (message) => {
    if (ws.current?.readyState === WebSocket.OPEN) {
      ws.current.send(JSON.stringify(message));
    }
  };

  return { messages, sendMessage, connectionStatus };
}

// Component using WebSocket
function ChatRoom() {
  const { messages, sendMessage, connectionStatus } = useWebSocket(
    'ws://localhost:8080'
  );
  const [input, setInput] = useState('');

  const handleSend = () => {
    sendMessage({ type: 'chat', text: input, timestamp: Date.now() });
    setInput('');
  };

  return (
    <div>
      <div>Status: {connectionStatus}</div>
      <div>
        {messages.map((msg, i) => (
          <div key={i}>{msg.text}</div>
        ))}
      </div>
      <input
        value={input}
        onChange={(e) => setInput(e.target.value)}
        onKeyPress={(e) => e.key === 'Enter' && handleSend()}
      />
      <button onClick={handleSend}>Send</button>
    </div>
  );
}

// Socket.io alternative
import io from 'socket.io-client';

function useSocketIO(url) {
  const [socket, setSocket] = useState(null);

  useEffect(() => {
    const newSocket = io(url, {
      reconnection: true,
      reconnectionDelay: 1000,
      reconnectionAttempts: 5,
    });

    newSocket.on('connect', () => {
      console.log('Socket.io connected');
    });

    setSocket(newSocket);

    return () => newSocket.close();
  }, [url]);

  return socket;
}

3.5 IndexedDB Offline Data Persistence

Feature API Description Use Case
open indexedDB.open(name, version) Opens database, creates if doesn't exist DB initialization
createObjectStore db.createObjectStore(name, { keyPath }) Creates table-like storage space Schema definition
transaction db.transaction([store], 'readwrite') Groups operations with ACID properties Data operations
add objectStore.add(data) Inserts new record, fails if key exists Insert operation
put objectStore.put(data) Updates or inserts record Upsert operation
get objectStore.get(key) Retrieves single record by key Read operation
getAll objectStore.getAll() Retrieves all records Bulk read
Index objectStore.createIndex(name, keyPath) Creates searchable index on field Query optimization

Example: IndexedDB wrapper with React hook

// IndexedDB helper class
class DatabaseManager {
  constructor(dbName, version = 1) {
    this.dbName = dbName;
    this.version = version;
    this.db = null;
  }

  async open() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.version);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        resolve(this.db);
      };

      request.onupgradeneeded = (event) => {
        const db = event.target.result;

        // Create object stores
        if (!db.objectStoreNames.contains('todos')) {
          const todoStore = db.createObjectStore('todos', { 
            keyPath: 'id', 
            autoIncrement: true 
          });
          todoStore.createIndex('completed', 'completed', { unique: false });
          todoStore.createIndex('createdAt', 'createdAt', { unique: false });
        }
      };
    });
  }

  async add(storeName, data) {
    const transaction = this.db.transaction([storeName], 'readwrite');
    const store = transaction.objectStore(storeName);
    return new Promise((resolve, reject) => {
      const request = store.add(data);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async getAll(storeName) {
    const transaction = this.db.transaction([storeName], 'readonly');
    const store = transaction.objectStore(storeName);
    return new Promise((resolve, reject) => {
      const request = store.getAll();
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async update(storeName, data) {
    const transaction = this.db.transaction([storeName], 'readwrite');
    const store = transaction.objectStore(storeName);
    return new Promise((resolve, reject) => {
      const request = store.put(data);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async delete(storeName, key) {
    const transaction = this.db.transaction([storeName], 'readwrite');
    const store = transaction.objectStore(storeName);
    return new Promise((resolve, reject) => {
      const request = store.delete(key);
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }
}

// React hook for IndexedDB
function useIndexedDB(dbName, storeName) {
  const [db, setDb] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const initDB = async () => {
      const manager = new DatabaseManager(dbName);
      await manager.open();
      setDb(manager);
      setLoading(false);
    };
    initDB();
  }, [dbName]);

  const addItem = async (data) => {
    if (!db) return;
    return db.add(storeName, data);
  };

  const getAllItems = async () => {
    if (!db) return [];
    return db.getAll(storeName);
  };

  const updateItem = async (data) => {
    if (!db) return;
    return db.update(storeName, data);
  };

  const deleteItem = async (key) => {
    if (!db) return;
    return db.delete(storeName, key);
  };

  return { addItem, getAllItems, updateItem, deleteItem, loading };
}

// Component using IndexedDB
function TodoApp() {
  const [todos, setTodos] = useState([]);
  const { addItem, getAllItems, updateItem, deleteItem, loading } = 
    useIndexedDB('TodoDB', 'todos');

  useEffect(() => {
    if (!loading) {
      loadTodos();
    }
  }, [loading]);

  const loadTodos = async () => {
    const items = await getAllItems();
    setTodos(items);
  };

  const handleAdd = async (text) => {
    const newTodo = {
      text,
      completed: false,
      createdAt: Date.now(),
    };
    await addItem(newTodo);
    loadTodos();
  };

  const handleToggle = async (todo) => {
    await updateItem({ ...todo, completed: !todo.completed });
    loadTodos();
  };

  if (loading) return <div>Loading...</div>;

  return (
    <div>
      {todos.map(todo => (
        <div key={todo.id}>
          <input
            type="checkbox"
            checked={todo.completed}
            onChange={() => handleToggle(todo)}
          />
          {todo.text}
        </div>
      ))}
    </div>
  );
}

3.6 Observer Pattern Event Emitters

Pattern Implementation Description Use Case
EventEmitter class EventEmitter { on, emit, off } Pub/sub pattern for decoupled communication Event-driven architecture
on/subscribe emitter.on('event', callback) Registers listener for specific event Event subscription
emit/publish emitter.emit('event', data) Triggers event, notifies all listeners Event broadcasting
off/unsubscribe emitter.off('event', callback) Removes specific event listener Cleanup
once emitter.once('event', callback) Listener fires only first time One-time events
Custom Events new CustomEvent('type', { detail }) Native browser event system DOM-based communication

Example: Event Emitter implementation with React integration

// EventEmitter class
class EventEmitter {
  constructor() {
    this.events = {};
  }

  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
    
    // Return unsubscribe function
    return () => this.off(event, listener);
  }

  once(event, listener) {
    const onceWrapper = (...args) => {
      listener(...args);
      this.off(event, onceWrapper);
    };
    return this.on(event, onceWrapper);
  }

  emit(event, ...args) {
    if (!this.events[event]) return;
    this.events[event].forEach(listener => listener(...args));
  }

  off(event, listenerToRemove) {
    if (!this.events[event]) return;
    this.events[event] = this.events[event].filter(
      listener => listener !== listenerToRemove
    );
  }

  removeAllListeners(event) {
    if (event) {
      delete this.events[event];
    } else {
      this.events = {};
    }
  }
}

// Global event bus instance
const eventBus = new EventEmitter();

// React hook for event subscriptions
function useEventListener(event, handler) {
  useEffect(() => {
    const unsubscribe = eventBus.on(event, handler);
    return unsubscribe; // Cleanup on unmount
  }, [event, handler]);
}

// Components using event bus
function UserProfile() {
  const [user, setUser] = useState(null);

  // Subscribe to user updates
  useEventListener('user:updated', (updatedUser) => {
    setUser(updatedUser);
  });

  return <div>{user?.name}</div>;
}

function UpdateButton() {
  const handleUpdate = () => {
    const newUser = { id: 1, name: 'John Updated' };
    // Emit event to all subscribers
    eventBus.emit('user:updated', newUser);
  };

  return <button onClick={handleUpdate}>Update User</button>;
}

// Custom DOM Events
function NotificationSystem() {
  useEffect(() => {
    const handleNotification = (event) => {
      console.log('Notification:', event.detail);
    };

    window.addEventListener('app:notification', handleNotification);
    return () => window.removeEventListener('app:notification', handleNotification);
  }, []);

  return <div>Notification System</div>;
}

function TriggerNotification() {
  const notify = () => {
    const event = new CustomEvent('app:notification', {
      detail: {
        message: 'Hello World',
        type: 'info',
        timestamp: Date.now(),
      },
    });
    window.dispatchEvent(event);
  };

  return <button onClick={notify}>Send Notification</button>;
}

// Advanced: Typed EventEmitter with TypeScript
interface Events {
  'user:login': (user: User) => void;
  'user:logout': () => void;
  'notification': (message: string) => void;
}

class TypedEventEmitter<T extends Record<string, (...args: any[]) => void>> {
  private events = new Map<keyof T, Set<T[keyof T]>>();

  on<K extends keyof T>(event: K, listener: T[K]): () => void {
    if (!this.events.has(event)) {
      this.events.set(event, new Set());
    }
    this.events.get(event)!.add(listener);
    return () => this.off(event, listener);
  }

  emit<K extends keyof T>(event: K, ...args: Parameters<T[K]>): void {
    this.events.get(event)?.forEach(listener => listener(...args));
  }

  off<K extends keyof T>(event: K, listener: T[K]): void {
    this.events.get(event)?.delete(listener);
  }
}

const typedEventBus = new TypedEventEmitter<Events>();
typedEventBus.on('user:login', (user) => console.log(user));
typedEventBus.emit('user:login', { id: 1, name: 'John' });

Data Flow Architecture Patterns

  • Flux/Redux - Predictable unidirectional flow, ideal for complex state management with time-travel debugging
  • GraphQL - Normalized cache eliminates data duplication, automatic updates across queries
  • React Query - Optimistic updates provide instant feedback, with automatic rollback on errors
  • WebSocket - Real-time bidirectional communication, essential for chat, live updates, collaborative editing
  • IndexedDB - Offline-first architecture with large data storage capacity (GBs), transactional operations
  • Observer Pattern - Decoupled component communication, reduces prop drilling, enables event-driven architecture

4. Event Handling Implementation Patterns

4.1 React SyntheticEvent preventDefault

Feature Syntax Description Use Case
SyntheticEvent event: React.MouseEvent<T> Cross-browser wrapper around native events Consistent event handling
preventDefault event.preventDefault() Prevents default browser action Form submission, link navigation
stopPropagation event.stopPropagation() Stops event bubbling to parent elements Nested clickable elements
currentTarget event.currentTarget Element handler is attached to Event delegation
target event.target Element that triggered the event Dynamic element access
nativeEvent event.nativeEvent Access underlying browser event Browser-specific features

Example: React SyntheticEvent with type-safe handlers

import React from 'react';

// Form submission with preventDefault
function LoginForm() {
  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault(); // Prevent page reload
    
    const formData = new FormData(event.currentTarget);
    const email = formData.get('email') as string;
    const password = formData.get('password') as string;
    
    console.log({ email, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" required />
      <input name="password" type="password" required />
      <button type="submit">Login</button>
    </form>
  );
}

// Click event with stopPropagation
function NestedButtons() {
  const handleParentClick = () => {
    console.log('Parent clicked');
  };

  const handleChildClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    event.stopPropagation(); // Prevent parent handler from firing
    console.log('Child clicked');
  };

  return (
    <div onClick={handleParentClick} style={{ padding: '20px', background: '#eee' }}>
      <button onClick={handleChildClick}>
        Click me (won't trigger parent)
      </button>
    </div>
  );
}

// Input change with proper typing
function SearchInput() {
  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const value = event.target.value;
    console.log('Search:', value);
  };

  const handleKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
    if (event.key === 'Enter') {
      event.preventDefault();
      console.log('Search submitted');
    }
  };

  return (
    <input
      type="text"
      onChange={handleChange}
      onKeyPress={handleKeyPress}
      placeholder="Search..."
    />
  );
}

// Mouse events with coordinates
function TrackMouse() {
  const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
    const { clientX, clientY, pageX, pageY } = event;
    console.log('Client:', clientX, clientY);
    console.log('Page:', pageX, pageY);
  };

  const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
    // Access native event for browser-specific features
    console.log('Native event:', event.nativeEvent);
    console.log('Button clicked:', event.button); // 0=left, 1=middle, 2=right
  };

  return (
    <div 
      onMouseMove={handleMouseMove}
      onClick={handleClick}
      style={{ width: '300px', height: '300px', background: '#f0f0f0' }}
    >
      Move mouse here
    </div>
  );
}

// Focus events
function FocusExample() {
  const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {
    event.target.style.borderColor = 'blue';
  };

  const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {
    event.target.style.borderColor = 'gray';
  };

  return (
    <input
      type="text"
      onFocus={handleFocus}
      onBlur={handleBlur}
      placeholder="Focus me"
    />
  );
}

4.2 Event Delegation querySelector addEventListener

Method Syntax Description Use Case
addEventListener element.addEventListener('click', handler) Attaches event listener to element DOM event handling
removeEventListener element.removeEventListener('click', handler) Removes specific event listener Cleanup, memory management
Event Delegation parent.addEventListener('click', (e) => {}) Single listener on parent handles child events Dynamic elements, performance
event.target e.target.matches('.class') Identifies which child triggered event Delegation filtering
capture phase addEventListener('click', fn, true) Event fires during capture before bubble Event interception
once option addEventListener('click', fn, { once: true }) Listener auto-removes after first call One-time events

Example: Event delegation with dynamic elements

// Event delegation for dynamic list items
function setupDynamicList() {
  const list = document.querySelector('#todo-list');

  // Single event listener on parent handles all children
  list.addEventListener('click', (event) => {
    const target = event.target;

    // Check if clicked element matches selector
    if (target.matches('.delete-btn')) {
      const todoItem = target.closest('.todo-item');
      todoItem.remove();
    }

    if (target.matches('.checkbox')) {
      const todoItem = target.closest('.todo-item');
      todoItem.classList.toggle('completed');
    }

    if (target.matches('.edit-btn')) {
      const todoItem = target.closest('.todo-item');
      editTodo(todoItem);
    }
  });

  // Add new items dynamically - no need for new listeners
  function addTodo(text) {
    const li = document.createElement('li');
    li.className = 'todo-item';
    li.innerHTML = `
      <input type="checkbox" class="checkbox">
      <span>${text}</span>
      <button class="edit-btn">Edit</button>
      <button class="delete-btn">Delete</button>
    `;
    list.appendChild(li);
  }
}

// Event listener options
function advancedEventListening() {
  const button = document.querySelector('#submit-btn');

  // Once option - auto-removes after first call
  button.addEventListener('click', handleClick, { once: true });

  // Passive option - improves scroll performance
  document.addEventListener('scroll', handleScroll, { passive: true });

  // Capture phase - fires before bubble phase
  document.addEventListener('click', captureHandler, true);

  function handleClick(event) {
    console.log('Button clicked once');
  }

  function handleScroll(event) {
    // Cannot call preventDefault in passive listener
    console.log('Scrolling...');
  }

  function captureHandler(event) {
    console.log('Capture phase:', event.target);
  }
}

// React hook for native event listeners
import { useEffect, useRef } from 'react';

function useEventListener(
  eventName: string,
  handler: (event: Event) => void,
  element: HTMLElement | Window = window
) {
  const savedHandler = useRef<(event: Event) => void>();

  useEffect(() => {
    savedHandler.current = handler;
  }, [handler]);

  useEffect(() => {
    const isSupported = element && element.addEventListener;
    if (!isSupported) return;

    const eventListener = (event: Event) => savedHandler.current?.(event);
    element.addEventListener(eventName, eventListener);

    return () => {
      element.removeEventListener(eventName, eventListener);
    };
  }, [eventName, element]);
}

// Usage in React component
function Component() {
  const divRef = useRef<HTMLDivElement>(null);

  useEventListener('mousemove', (event) => {
    console.log('Mouse position:', event.clientX, event.clientY);
  }, divRef.current);

  useEventListener('resize', () => {
    console.log('Window resized');
  }, window);

  return <div ref={divRef}>Content</div>;
}

// Event delegation with closest()
document.querySelector('.container').addEventListener('click', (event) => {
  // Find closest ancestor matching selector
  const card = event.target.closest('.card');
  if (card) {
    console.log('Card clicked:', card.dataset.id);
  }
});

4.3 Custom Events dispatch CustomEvent

API Syntax Description Use Case
CustomEvent new CustomEvent('type', { detail }) Creates custom event with data payload Component communication
detail { detail: { data: value } } Payload data attached to event Data passing
bubbles { bubbles: true } Event propagates up DOM tree Event bubbling
dispatchEvent element.dispatchEvent(event) Triggers custom event on element Event emission
composed { composed: true } Event crosses shadow DOM boundary Web Components

Example: Custom events for component communication

// Creating and dispatching custom events
class NotificationSystem {
  static show(message, type = 'info') {
    const event = new CustomEvent('app:notification', {
      detail: {
        message,
        type,
        timestamp: Date.now(),
      },
      bubbles: true,
      composed: true,
    });
    document.dispatchEvent(event);
  }
}

// Listening to custom events
document.addEventListener('app:notification', (event) => {
  const { message, type, timestamp } = event.detail;
  console.log(`[${type}] ${message} at ${new Date(timestamp)}`);
  showToast(message, type);
});

// Usage
NotificationSystem.show('User logged in', 'success');
NotificationSystem.show('Failed to load data', 'error');

// React hook for custom events
function useCustomEvent(eventName, handler) {
  useEffect(() => {
    const eventHandler = (event) => handler(event.detail);
    document.addEventListener(eventName, eventHandler);
    return () => document.removeEventListener(eventName, eventHandler);
  }, [eventName, handler]);
}

// Component emitting custom event
function CartButton() {
  const addToCart = (product) => {
    const event = new CustomEvent('cart:add', {
      detail: { product, quantity: 1 },
      bubbles: true,
    });
    document.dispatchEvent(event);
  };

  return <button onClick={() => addToCart({ id: 1, name: 'Product' })}>Add to Cart</button>;
}

// Component listening to custom event
function CartCounter() {
  const [count, setCount] = useState(0);

  useCustomEvent('cart:add', (detail) => {
    setCount(prev => prev + detail.quantity);
  });

  return <div>Cart: {count}</div>;
}

// Web Component with custom events
class UserCard extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `
      <div class="card">
        <h3>${this.getAttribute('name')}</h3>
        <button id="follow-btn">Follow</button>
      </div>
    `;

    this.querySelector('#follow-btn').addEventListener('click', () => {
      const event = new CustomEvent('user:follow', {
        detail: {
          userId: this.getAttribute('user-id'),
          userName: this.getAttribute('name'),
        },
        bubbles: true,
        composed: true, // Cross shadow DOM
      });
      this.dispatchEvent(event);
    });
  }
}

customElements.define('user-card', UserCard);

// Listen to web component events
document.addEventListener('user:follow', (event) => {
  console.log('Following user:', event.detail.userName);
});

// TypeScript typed custom events
interface AppEventMap {
  'cart:add': CustomEvent<{ product: Product; quantity: number }>;
  'user:login': CustomEvent<{ user: User }>;
  'notification': CustomEvent<{ message: string; type: string }>;
}

function dispatchTypedEvent<K extends keyof AppEventMap>(
  type: K,
  detail: AppEventMap[K]['detail']
) {
  const event = new CustomEvent(type, { detail, bubbles: true });
  document.dispatchEvent(event);
}

// Type-safe usage
dispatchTypedEvent('cart:add', { product: myProduct, quantity: 2 });

4.4 Debounce Throttle Lodash useDebounce

Technique Behavior Description Use Case
Debounce Delays execution until pause in events Waits for event stream to stop, then executes once Search input, window resize
Throttle Limits execution rate to interval Executes at most once per time period Scroll, mouse move, animation
lodash.debounce debounce(fn, delay, options) Debounce with leading/trailing edge control Production-ready utility
lodash.throttle throttle(fn, interval, options) Throttle with leading/trailing edge control Rate limiting
useDebounce hook const debounced = useDebounce(value, delay) React hook for debounced values React state debouncing

Example: Debounce and throttle implementations with React hooks

// Pure JavaScript debounce implementation
function debounce(func, delay) {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

// Pure JavaScript throttle implementation
function throttle(func, interval) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= interval) {
      lastCall = now;
      func.apply(this, args);
    }
  };
}

// Debounced search input
const searchInput = document.querySelector('#search');
const debouncedSearch = debounce((query) => {
  console.log('Searching for:', query);
  fetchResults(query);
}, 500);

searchInput.addEventListener('input', (e) => {
  debouncedSearch(e.target.value);
});

// Throttled scroll handler
const handleScroll = throttle(() => {
  console.log('Scroll position:', window.scrollY);
  updateScrollPosition();
}, 100);

window.addEventListener('scroll', handleScroll);

// React useDebounce hook
function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(handler);
  }, [value, delay]);

  return debouncedValue;
}

// React useThrottle hook
function useThrottle<T>(value: T, interval: number): T {
  const [throttledValue, setThrottledValue] = useState(value);
  const lastUpdated = useRef(Date.now());

  useEffect(() => {
    const now = Date.now();
    const timeSinceLastUpdate = now - lastUpdated.current;

    if (timeSinceLastUpdate >= interval) {
      lastUpdated.current = now;
      setThrottledValue(value);
    } else {
      const timeoutId = setTimeout(() => {
        lastUpdated.current = Date.now();
        setThrottledValue(value);
      }, interval - timeSinceLastUpdate);

      return () => clearTimeout(timeoutId);
    }
  }, [value, interval]);

  return throttledValue;
}

// Debounced search component
function SearchBox() {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  useEffect(() => {
    if (debouncedSearchTerm) {
      console.log('Searching:', debouncedSearchTerm);
      fetchSearchResults(debouncedSearchTerm);
    }
  }, [debouncedSearchTerm]);

  return (
    <input
      type="text"
      value={searchTerm}
      onChange={(e) => setSearchTerm(e.target.value)}
      placeholder="Search..."
    />
  );
}

// Throttled scroll component
function ScrollTracker() {
  const [scrollY, setScrollY] = useState(0);
  const throttledScrollY = useThrottle(scrollY, 100);

  useEffect(() => {
    const handleScroll = () => setScrollY(window.scrollY);
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  return <div>Scroll Position: {throttledScrollY}px</div>;
}

// Using lodash debounce/throttle
import { debounce, throttle } from 'lodash';

function SearchWithLodash() {
  const debouncedSearch = useMemo(
    () => debounce((query) => {
      console.log('Searching:', query);
    }, 500, {
      leading: false,
      trailing: true,
    }),
    []
  );

  useEffect(() => {
    return () => debouncedSearch.cancel(); // Cleanup
  }, [debouncedSearch]);

  return (
    <input
      onChange={(e) => debouncedSearch(e.target.value)}
      placeholder="Search..."
    />
  );
}

// Advanced: Debounce with abort controller
function useDebouncedCallback(callback, delay) {
  const callbackRef = useRef(callback);
  const timeoutRef = useRef();
  const abortControllerRef = useRef();

  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);

  return useCallback((...args) => {
    // Cancel previous request
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }

    clearTimeout(timeoutRef.current);

    timeoutRef.current = setTimeout(() => {
      abortControllerRef.current = new AbortController();
      callbackRef.current(...args, abortControllerRef.current.signal);
    }, delay);
  }, [delay]);
}

4.5 Keyboard Navigation Tab Index Focus

Property/Method Syntax Description Use Case
tabIndex tabIndex={0} Makes element keyboard focusable in DOM order Custom interactive elements
tabIndex -1 tabIndex={-1} Focusable programmatically but not via Tab Modal focus management
focus() element.focus() Programmatically sets focus to element Auto-focus input
onKeyDown onKeyDown={(e) => {}} Handles keyboard key press events Keyboard shortcuts
roving tabindex One item tabIndex={0}, others -1 Single Tab stop for component group Toolbars, menus, lists
aria-activedescendant aria-activedescendant="id" Indicates active descendant without focus move Listbox, combobox

Example: Keyboard navigation with roving tabindex

// Simple keyboard navigation
function KeyboardShortcuts() {
  const handleKeyDown = (event: React.KeyboardEvent) => {
    // Ctrl/Cmd + S = Save
    if ((event.ctrlKey || event.metaKey) && event.key === 's') {
      event.preventDefault();
      handleSave();
    }

    // Escape = Close modal
    if (event.key === 'Escape') {
      handleClose();
    }

    // Arrow keys = Navigation
    if (event.key === 'ArrowDown') {
      event.preventDefault();
      navigateDown();
    }
  };

  return (
    <div onKeyDown={handleKeyDown} tabIndex={0}>
      Press Ctrl+S to save
    </div>
  );
}

// Roving tabindex implementation
function Toolbar() {
  const [focusedIndex, setFocusedIndex] = useState(0);
  const buttonsRef = useRef<(HTMLButtonElement | null)[]>([]);

  const buttons = ['Cut', 'Copy', 'Paste', 'Undo', 'Redo'];

  const handleKeyDown = (event: React.KeyboardEvent, index: number) => {
    let newIndex = index;

    switch (event.key) {
      case 'ArrowRight':
        event.preventDefault();
        newIndex = Math.min(index + 1, buttons.length - 1);
        break;
      case 'ArrowLeft':
        event.preventDefault();
        newIndex = Math.max(index - 1, 0);
        break;
      case 'Home':
        event.preventDefault();
        newIndex = 0;
        break;
      case 'End':
        event.preventDefault();
        newIndex = buttons.length - 1;
        break;
      default:
        return;
    }

    setFocusedIndex(newIndex);
    buttonsRef.current[newIndex]?.focus();
  };

  return (
    <div role="toolbar" aria-label="Text formatting">
      {buttons.map((label, index) => (
        <button
          key={label}
          ref={(el) => (buttonsRef.current[index] = el)}
          tabIndex={index === focusedIndex ? 0 : -1}
          onKeyDown={(e) => handleKeyDown(e, index)}
          onClick={() => console.log(`${label} clicked`)}
        >
          {label}
        </button>
      ))}
    </div>
  );
}

// Focus trap for modal
function useFocusTrap(ref: React.RefObject<HTMLElement>) {
  useEffect(() => {
    const element = ref.current;
    if (!element) return;

    const focusableElements = element.querySelectorAll(
      'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
    );
    const firstElement = focusableElements[0] as HTMLElement;
    const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;

    const handleTabKey = (e: KeyboardEvent) => {
      if (e.key !== 'Tab') return;

      if (e.shiftKey) {
        if (document.activeElement === firstElement) {
          lastElement.focus();
          e.preventDefault();
        }
      } else {
        if (document.activeElement === lastElement) {
          firstElement.focus();
          e.preventDefault();
        }
      }
    };

    element.addEventListener('keydown', handleTabKey);
    firstElement?.focus();

    return () => element.removeEventListener('keydown', handleTabKey);
  }, [ref]);
}

// Modal with focus management
function Modal({ isOpen, onClose, children }) {
  const modalRef = useRef<HTMLDivElement>(null);
  const previousFocusRef = useRef<HTMLElement | null>(null);

  useFocusTrap(modalRef);

  useEffect(() => {
    if (isOpen) {
      previousFocusRef.current = document.activeElement as HTMLElement;
    } else if (previousFocusRef.current) {
      previousFocusRef.current.focus();
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <div
      ref={modalRef}
      role="dialog"
      aria-modal="true"
      onKeyDown={(e) => e.key === 'Escape' && onClose()}
    >
      {children}
      <button onClick={onClose}>Close</button>
    </div>
  );
}

// Custom dropdown with keyboard navigation
function Dropdown({ options }: { options: string[] }) {
  const [isOpen, setIsOpen] = useState(false);
  const [selectedIndex, setSelectedIndex] = useState(-1);
  const listRef = useRef<HTMLUListElement>(null);

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (!isOpen) {
      if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
        e.preventDefault();
        setIsOpen(true);
        setSelectedIndex(0);
      }
      return;
    }

    switch (e.key) {
      case 'ArrowDown':
        e.preventDefault();
        setSelectedIndex((prev) => Math.min(prev + 1, options.length - 1));
        break;
      case 'ArrowUp':
        e.preventDefault();
        setSelectedIndex((prev) => Math.max(prev - 1, 0));
        break;
      case 'Enter':
      case ' ':
        e.preventDefault();
        console.log('Selected:', options[selectedIndex]);
        setIsOpen(false);
        break;
      case 'Escape':
        e.preventDefault();
        setIsOpen(false);
        break;
    }
  };

  return (
    <div>
      <button
        onKeyDown={handleKeyDown}
        onClick={() => setIsOpen(!isOpen)}
        aria-haspopup="listbox"
        aria-expanded={isOpen}
      >
        Select option
      </button>
      {isOpen && (
        <ul ref={listRef} role="listbox">
          {options.map((option, index) => (
            <li
              key={option}
              role="option"
              aria-selected={index === selectedIndex}
              style={{
                background: index === selectedIndex ? '#e0e0e0' : 'transparent',
              }}
            >
              {option}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

4.6 Touch Events Mobile Gesture Handling

Event Trigger Description Use Case
touchstart Finger touches screen Fires when touch begins Gesture initiation
touchmove Finger moves on screen Fires repeatedly during touch movement Drag, swipe tracking
touchend Finger leaves screen Fires when touch ends Gesture completion
touches event.touches Array of all current touch points Multi-touch gestures
changedTouches event.changedTouches Touches that changed in this event Specific touch handling
Pointer Events onPointerDown/Move/Up Unified API for mouse, touch, pen Cross-device input

Example: Touch gestures with swipe and pinch detection

// Swipe gesture detection
function useSwipe(onSwipeLeft, onSwipeRight, threshold = 50) {
  const touchStart = useRef<{ x: number; y: number } | null>(null);

  const handleTouchStart = (e: React.TouchEvent) => {
    touchStart.current = {
      x: e.touches[0].clientX,
      y: e.touches[0].clientY,
    };
  };

  const handleTouchEnd = (e: React.TouchEvent) => {
    if (!touchStart.current) return;

    const touchEnd = {
      x: e.changedTouches[0].clientX,
      y: e.changedTouches[0].clientY,
    };

    const deltaX = touchEnd.x - touchStart.current.x;
    const deltaY = Math.abs(touchEnd.y - touchStart.current.y);

    // Horizontal swipe (deltaX > deltaY)
    if (Math.abs(deltaX) > threshold && Math.abs(deltaX) > deltaY) {
      if (deltaX > 0) {
        onSwipeRight?.();
      } else {
        onSwipeLeft?.();
      }
    }

    touchStart.current = null;
  };

  return { handleTouchStart, handleTouchEnd };
}

// Swipeable component
function SwipeableCard() {
  const { handleTouchStart, handleTouchEnd } = useSwipe(
    () => console.log('Swiped left'),
    () => console.log('Swiped right')
  );

  return (
    <div
      onTouchStart={handleTouchStart}
      onTouchEnd={handleTouchEnd}
      style={{
        width: '300px',
        height: '200px',
        background: '#f0f0f0',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      Swipe me left or right
    </div>
  );
}

// Draggable element with touch
function DraggableElement() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const [isDragging, setIsDragging] = useState(false);
  const startPos = useRef({ x: 0, y: 0 });
  const currentPos = useRef({ x: 0, y: 0 });

  const handleTouchStart = (e: React.TouchEvent) => {
    setIsDragging(true);
    startPos.current = {
      x: e.touches[0].clientX - position.x,
      y: e.touches[0].clientY - position.y,
    };
  };

  const handleTouchMove = (e: React.TouchEvent) => {
    if (!isDragging) return;

    currentPos.current = {
      x: e.touches[0].clientX - startPos.current.x,
      y: e.touches[0].clientY - startPos.current.y,
    };

    setPosition(currentPos.current);
  };

  const handleTouchEnd = () => {
    setIsDragging(false);
  };

  return (
    <div
      onTouchStart={handleTouchStart}
      onTouchMove={handleTouchMove}
      onTouchEnd={handleTouchEnd}
      style={{
        position: 'absolute',
        left: `${position.x}px`,
        top: `${position.y}px`,
        width: '100px',
        height: '100px',
        background: '#4CAF50',
        cursor: isDragging ? 'grabbing' : 'grab',
        touchAction: 'none', // Prevent default touch behaviors
      }}
    >
      Drag me
    </div>
  );
}

// Pinch-to-zoom gesture
function usePinchZoom() {
  const [scale, setScale] = useState(1);
  const initialDistance = useRef<number | null>(null);
  const initialScale = useRef(1);

  const getDistance = (touch1: React.Touch, touch2: React.Touch) => {
    const dx = touch1.clientX - touch2.clientX;
    const dy = touch1.clientY - touch2.clientY;
    return Math.sqrt(dx * dx + dy * dy);
  };

  const handleTouchStart = (e: React.TouchEvent) => {
    if (e.touches.length === 2) {
      initialDistance.current = getDistance(e.touches[0], e.touches[1]);
      initialScale.current = scale;
    }
  };

  const handleTouchMove = (e: React.TouchEvent) => {
    if (e.touches.length === 2 && initialDistance.current) {
      const currentDistance = getDistance(e.touches[0], e.touches[1]);
      const newScale = (currentDistance / initialDistance.current) * initialScale.current;
      setScale(Math.min(Math.max(newScale, 0.5), 3)); // Limit scale 0.5x to 3x
    }
  };

  const handleTouchEnd = () => {
    initialDistance.current = null;
  };

  return { scale, handleTouchStart, handleTouchMove, handleTouchEnd };
}

// Zoomable image
function ZoomableImage({ src }: { src: string }) {
  const { scale, handleTouchStart, handleTouchMove, handleTouchEnd } = usePinchZoom();

  return (
    <div
      onTouchStart={handleTouchStart}
      onTouchMove={handleTouchMove}
      onTouchEnd={handleTouchEnd}
      style={{
        overflow: 'hidden',
        touchAction: 'none',
      }}
    >
      <img
        src={src}
        alt="Zoomable"
        style={{
          transform: `scale(${scale})`,
          transformOrigin: 'center center',
          transition: 'transform 0.1s',
        }}
      />
    </div>
  );
}

// Unified pointer events (mouse + touch + pen)
function UnifiedPointerHandler() {
  const [isPressed, setIsPressed] = useState(false);
  const [position, setPosition] = useState({ x: 0, y: 0 });

  const handlePointerDown = (e: React.PointerEvent) => {
    setIsPressed(true);
    (e.target as HTMLElement).setPointerCapture(e.pointerId);
  };

  const handlePointerMove = (e: React.PointerEvent) => {
    if (isPressed) {
      setPosition({ x: e.clientX, y: e.clientY });
    }
  };

  const handlePointerUp = (e: React.PointerEvent) => {
    setIsPressed(false);
    (e.target as HTMLElement).releasePointerCapture(e.pointerId);
  };

  return (
    <div
      onPointerDown={handlePointerDown}
      onPointerMove={handlePointerMove}
      onPointerUp={handlePointerUp}
      style={{
        width: '100%',
        height: '400px',
        background: '#f0f0f0',
        position: 'relative',
        touchAction: 'none',
      }}
    >
      <div
        style={{
          position: 'absolute',
          left: `${position.x}px`,
          top: `${position.y}px`,
          width: '20px',
          height: '20px',
          background: isPressed ? 'red' : 'blue',
          borderRadius: '50%',
          transform: 'translate(-50%, -50%)',
        }}
      />
    </div>
  );
}

Event Handling Best Practices

  • SyntheticEvent - React's cross-browser event wrapper, use preventDefault() for form submissions
  • Event Delegation - Single listener on parent for dynamic children, improves performance
  • Custom Events - Enable decoupled component communication via browser's event system
  • Debounce - For search, resize, input validation (wait for pause)
  • Throttle - For scroll, mouse move, animations (limit execution rate)
  • Keyboard Navigation - Roving tabindex for toolbars, arrow keys for lists, focus trap for modals
  • Touch Events - Use pointer events for unified mouse/touch/pen handling, prevent default behaviors with touchAction

5. Modern Rendering Strategies Implementation

5.1 Next.js 14 App Router SSR SSG NEW

Feature Syntax Description Use Case
App Router app/ directory structure File-system based routing with layouts and nested routes Modern Next.js architecture
Server Components async function Page() {} Components that render on server by default Zero JS to client, SEO
generateStaticParams export async function generateStaticParams() Static generation of dynamic routes at build time SSG for dynamic routes
Dynamic Rendering export const dynamic = 'force-dynamic' SSR on every request, no caching Personalized content
Revalidation export const revalidate = 60 Incremental Static Regeneration (ISR) interval Periodic content updates
Loading UI loading.tsx Instant loading state while streaming Progressive rendering
Error Boundaries error.tsx Route-level error handling Graceful error recovery

Example: Next.js 14 App Router with SSR, SSG, and ISR

// app/layout.tsx - Root layout with metadata
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'My App',
  description: 'Next.js 14 App Router Example',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <nav>Navigation</nav>
        {children}
      </body>
    </html>
  );
}

// app/page.tsx - Static page (SSG by default)
export default async function HomePage() {
  const data = await fetch('https://api.example.com/posts', {
    cache: 'force-cache', // SSG: cached at build time
  }).then(res => res.json());

  return (
    <div>
      <h1>Home Page (Static)</h1>
      {data.map(post => <div key={post.id}>{post.title}</div>)}
    </div>
  );
}

// app/posts/[id]/page.tsx - Dynamic route with SSG
interface PageProps {
  params: { id: string };
}

// Generate static paths at build time
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then(res => res.json());
  
  return posts.map((post: any) => ({
    id: post.id.toString(),
  }));
}

// Revalidate every 60 seconds (ISR)
export const revalidate = 60;

export default async function PostPage({ params }: PageProps) {
  const post = await fetch(`https://api.example.com/posts/${params.id}`, {
    next: { revalidate: 60 }, // Per-fetch revalidation
  }).then(res => res.json());

  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

// app/dashboard/page.tsx - Dynamic SSR (no cache)
export const dynamic = 'force-dynamic';

export default async function DashboardPage() {
  const user = await getCurrentUser();
  const stats = await fetch(`https://api.example.com/stats/${user.id}`, {
    cache: 'no-store', // SSR: fetch on every request
  }).then(res => res.json());

  return (
    <div>
      <h1>Dashboard (Dynamic SSR)</h1>
      <p>Views: {stats.views}</p>
    </div>
  );
}

// app/products/loading.tsx - Loading state
export default function Loading() {
  return <div>Loading products...</div>;
}

// app/products/error.tsx - Error boundary
'use client';

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={reset}>Try again</button>
    </div>
  );
}

// Client Component with 'use client'
'use client';

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

5.2 Vite React SPA Client Rendering

Feature Configuration Description Benefit
HMR Hot Module Replacement Instant updates without full page reload Fast development
ESBuild transform: { jsx: 'react' } Lightning-fast JavaScript/TypeScript compilation 10-100x faster than webpack
Code Splitting import() Dynamic imports for lazy loading Smaller initial bundle
Tree Shaking build.rollupOptions Dead code elimination in production Optimized bundle size
CSS Modules .module.css Scoped CSS with automatic class names No style conflicts
Environment Variables import.meta.env.VITE_* Access env vars prefixed with VITE_ Configuration management

Example: Vite React SPA with optimized configuration

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    react({
      // Fast Refresh for HMR
      fastRefresh: true,
    }),
    visualizer({
      open: true,
      gzipSize: true,
      brotliSize: true,
    }),
  ],
  build: {
    // Target modern browsers
    target: 'esnext',
    // Manual chunk splitting
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          router: ['react-router-dom'],
          ui: ['@mui/material'],
        },
      },
    },
    // Source maps for production debugging
    sourcemap: true,
    // Chunk size warning limit
    chunkSizeWarningLimit: 1000,
  },
  server: {
    port: 3000,
    open: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
  },
  resolve: {
    alias: {
      '@': '/src',
      '@components': '/src/components',
      '@utils': '/src/utils',
    },
  },
});

// src/main.tsx - Entry point
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// src/App.tsx - Lazy loading routes
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>Loading...</div>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/settings" element={<Settings />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

export default App;

// .env - Environment variables
VITE_API_URL=https://api.example.com
VITE_API_KEY=abc123

// Using environment variables
const apiUrl = import.meta.env.VITE_API_URL;
const apiKey = import.meta.env.VITE_API_KEY;

// CSS Modules - Button.module.css
.button {
  padding: 10px 20px;
  background: blue;
}

// Button.tsx
import styles from './Button.module.css';

function Button() {
  return <button className={styles.button}>Click Me</button>;
}

5.3 Nuxt 3 Universal Rendering

Feature Syntax Description Use Case
Universal Rendering SSR + Hydration Server renders HTML, client hydrates with interactivity Best of both worlds
Auto Imports No import statements needed Components, composables auto-imported Less boilerplate
useFetch const { data } = await useFetch('/api') Universal data fetching with SSR support Isomorphic data fetching
useAsyncData useAsyncData('key', () => $fetch()) Manual async data fetching with caching Complex data needs
Server Routes server/api/*.ts API routes built into Nuxt Full-stack framework
Middleware middleware/*.ts Route guards for auth, validation Navigation control

Example: Nuxt 3 universal rendering with data fetching

// nuxt.config.ts
export default defineNuxtConfig({
  // Rendering mode
  ssr: true,
  
  // Hybrid rendering per route
  routeRules: {
    '/': { prerender: true }, // SSG
    '/admin/**': { ssr: false }, // SPA
    '/api/**': { cors: true },
    '/blog/**': { swr: 3600 }, // ISR with 1h cache
  },

  // App configuration
  app: {
    head: {
      title: 'Nuxt 3 App',
      meta: [
        { name: 'description', content: 'Universal rendering example' }
      ],
    },
  },

  // Modules
  modules: ['@nuxtjs/tailwindcss', '@pinia/nuxt'],

  // Auto imports
  imports: {
    dirs: ['composables', 'utils'],
  },
});

// pages/index.vue - SSR page with data fetching
<template>
  <div>
    <h1>Posts</h1>
    <div v-for="post in posts" :key="post.id">
      <h2>{{ post.title }}</h2>
      <p>{{ post.content }}</p>
    </div>
  </div>
</template>

<script setup>
// Auto-imported useFetch
const { data: posts } = await useFetch('/api/posts');

// SEO metadata
useHead({
  title: 'Blog Posts',
  meta: [
    { name: 'description', content: 'Latest blog posts' }
  ],
});
</script>

// pages/post/[id].vue - Dynamic route with SSR
<template>
  <article>
    <h1>{{ post?.title }}</h1>
    <p>{{ post?.content }}</p>
  </article>
</template>

<script setup>
const route = useRoute();

const { data: post } = await useAsyncData(
  `post-${route.params.id}`,
  () => $fetch(`/api/posts/${route.params.id}`)
);
</script>

// server/api/posts.ts - API route
export default defineEventHandler(async (event) => {
  const posts = await fetchPostsFromDB();
  return posts;
});

// server/api/posts/[id].ts - Dynamic API route
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id');
  const post = await fetchPostById(id);
  
  if (!post) {
    throw createError({
      statusCode: 404,
      statusMessage: 'Post not found',
    });
  }
  
  return post;
});

// composables/useAuth.ts - Auto-imported composable
export const useAuth = () => {
  const user = useState('user', () => null);

  const login = async (credentials) => {
    const data = await $fetch('/api/auth/login', {
      method: 'POST',
      body: credentials,
    });
    user.value = data.user;
  };

  const logout = () => {
    user.value = null;
  };

  return { user, login, logout };
};

// middleware/auth.ts - Route middleware
export default defineNuxtRouteMiddleware((to, from) => {
  const { user } = useAuth();

  if (!user.value && to.path !== '/login') {
    return navigateTo('/login');
  }
});

// pages/dashboard.vue - Protected route
<template>
  <div>Dashboard for {{ user?.name }}</div>
</template>

<script setup>
definePageMeta({
  middleware: 'auth',
});

const { user } = useAuth();
</script>

5.4 React 18 Concurrent Features NEW

Feature API Description Use Case
Concurrent Rendering createRoot() Interruptible rendering for better UX Responsive UI
useTransition const [isPending, startTransition] = useTransition() Marks updates as non-urgent, allows interruption Smooth UI transitions
useDeferredValue const deferredValue = useDeferredValue(value) Defers value updates to prioritize urgent renders Debounced rendering
Suspense <Suspense fallback={...}> Declarative loading states for async components Code splitting, data fetching
startTransition startTransition(() => setState()) Marks state updates as low-priority Heavy computations
useId const id = useId() Generates unique IDs for SSR compatibility Accessible forms

Example: React 18 concurrent features in action

// main.tsx - Concurrent mode with createRoot
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root')!;
const root = createRoot(container);
root.render(<App />);

// useTransition for non-blocking updates
import { useState, useTransition } from 'react';

function SearchResults() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  const [isPending, startTransition] = useTransition();

  const handleSearch = (value: string) => {
    setQuery(value); // Urgent: update input immediately

    // Non-urgent: expensive search operation
    startTransition(() => {
      const filtered = expensiveSearch(value);
      setResults(filtered);
    });
  };

  return (
    <div>
      <input
        value={query}
        onChange={(e) => handleSearch(e.target.value)}
        placeholder="Search..."
      />
      {isPending && <div>Searching...</div>}
      <ul>
        {results.map(item => <li key={item.id}>{item.name}</li>)}
      </ul>
    </div>
  );
}

// useDeferredValue for responsive UI
function ProductList({ searchQuery }: { searchQuery: string }) {
  // Deferred value updates slower, keeps input responsive
  const deferredQuery = useDeferredValue(searchQuery);
  const [products, setProducts] = useState([]);

  useEffect(() => {
    // Heavy filtering operation uses deferred value
    const filtered = expensiveFilter(deferredQuery);
    setProducts(filtered);
  }, [deferredQuery]);

  return (
    <div>
      {products.map(product => (
        <div key={product.id}>{product.name}</div>
      ))}
    </div>
  );
}

// Suspense for code splitting and data fetching
import { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading component...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

// Suspense with data fetching (using libraries like Relay, React Query)
function UserProfile({ userId }: { userId: string }) {
  return (
    <Suspense fallback={<ProfileSkeleton />}>
      <ProfileDetails userId={userId} />
      <Suspense fallback={<PostsSkeleton />}>
        <UserPosts userId={userId} />
      </Suspense>
    </Suspense>
  );
}

// useId for SSR-safe unique IDs
function FormField({ label }: { label: string }) {
  const id = useId();

  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input id={id} type="text" />
    </div>
  );
}

// Combining concurrent features
function Dashboard() {
  const [activeTab, setActiveTab] = useState('overview');
  const [isPending, startTransition] = useTransition();

  const handleTabChange = (tab: string) => {
    startTransition(() => {
      setActiveTab(tab);
    });
  };

  return (
    <div>
      <nav>
        <button onClick={() => handleTabChange('overview')}>Overview</button>
        <button onClick={() => handleTabChange('analytics')}>Analytics</button>
        <button onClick={() => handleTabChange('reports')}>Reports</button>
      </nav>
      {isPending && <div>Loading...</div>}
      <Suspense fallback={<div>Loading content...</div>}>
        <TabContent tab={activeTab} />
      </Suspense>
    </div>
  );
}

// Automatic batching (React 18)
function Counter() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  const handleClick = () => {
    // Both updates batched automatically (even in async)
    setTimeout(() => {
      setCount(c => c + 1);
      setFlag(f => !f);
      // Only one re-render
    }, 1000);
  };

  return <button onClick={handleClick}>Count: {count}</button>;
}

5.5 Virtual Scrolling react-window

Component Syntax Description Use Case
FixedSizeList <FixedSizeList height={} itemCount={} itemSize={}> List with fixed-height items Uniform item heights
VariableSizeList <VariableSizeList itemSize={(index) => {}}> List with dynamic item heights Variable content sizes
FixedSizeGrid <FixedSizeGrid columnCount={} rowCount={}> Grid with fixed cell sizes Spreadsheets, tables
Windowing Only renders visible items Renders only what's in viewport + buffer Performance with huge lists
react-virtuoso <Virtuoso data={} itemContent={}> Advanced virtualization with features Complex scrolling needs

Example: Virtual scrolling for large datasets

import { FixedSizeList, VariableSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';

// Fixed-size list
function VirtualList({ items }: { items: any[] }) {
  const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => (
    <div style={style}>
      {items[index].name}
    </div>
  );

  return (
    <FixedSizeList
      height={600}
      itemCount={items.length}
      itemSize={50}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
}

// Variable-size list
function VariableList({ items }: { items: any[] }) {
  const itemSizes = useRef<number[]>([]);

  const getItemSize = (index: number) => {
    // Calculate or cache item heights
    return itemSizes.current[index] || 50;
  };

  const Row = ({ index, style }: any) => {
    const rowRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
      if (rowRef.current) {
        itemSizes.current[index] = rowRef.current.offsetHeight;
      }
    }, [index]);

    return (
      <div ref={rowRef} style={style}>
        {items[index].content}
      </div>
    );
  };

  return (
    <VariableSizeList
      height={600}
      itemCount={items.length}
      itemSize={getItemSize}
      width="100%"
    >
      {Row}
    </VariableSizeList>
  );
}

// With AutoSizer for responsive dimensions
function ResponsiveList({ items }: { items: any[] }) {
  return (
    <div style={{ height: '100vh' }}>
      <AutoSizer>
        {({ height, width }) => (
          <FixedSizeList
            height={height}
            width={width}
            itemCount={items.length}
            itemSize={50}
          >
            {({ index, style }) => (
              <div style={style}>{items[index].name}</div>
            )}
          </FixedSizeList>
        )}
      </AutoSizer>
    </div>
  );
}

// Infinite scroll with react-window
function InfiniteList({ loadMore }: { loadMore: () => Promise<void> }) {
  const [items, setItems] = useState<any[]>([]);
  const [hasMore, setHasMore] = useState(true);
  const [isLoading, setIsLoading] = useState(false);

  const loadMoreItems = async () => {
    if (isLoading || !hasMore) return;
    
    setIsLoading(true);
    const newItems = await loadMore();
    setItems(prev => [...prev, ...newItems]);
    setHasMore(newItems.length > 0);
    setIsLoading(false);
  };

  const isItemLoaded = (index: number) => !hasMore || index < items.length;

  return (
    <InfiniteLoader
      isItemLoaded={isItemLoaded}
      itemCount={hasMore ? items.length + 1 : items.length}
      loadMoreItems={loadMoreItems}
    >
      {({ onItemsRendered, ref }) => (
        <FixedSizeList
          height={600}
          itemCount={items.length}
          itemSize={50}
          onItemsRendered={onItemsRendered}
          ref={ref}
          width="100%"
        >
          {({ index, style }) => (
            <div style={style}>
              {isItemLoaded(index) ? items[index].name : 'Loading...'}
            </div>
          )}
        </FixedSizeList>
      )}
    </InfiniteLoader>
  );
}

// react-virtuoso (alternative with better features)
import { Virtuoso } from 'react-virtuoso';

function VirtuosoList({ items }: { items: any[] }) {
  return (
    <Virtuoso
      style={{ height: '600px' }}
      data={items}
      itemContent={(index, item) => (
        <div>
          <h3>{item.title}</h3>
          <p>{item.description}</p>
        </div>
      )}
      endReached={() => console.log('Reached end')}
    />
  );
}

5.6 Streaming SSR Suspense Components

Feature Implementation Description Benefit
Streaming SSR renderToPipeableStream() Sends HTML chunks as they're ready Faster Time to First Byte
Selective Hydration React 18 automatic Hydrates components in priority order Interactive sooner
Suspense Boundaries <Suspense fallback={...}> Stream different parts independently Progressive enhancement
Server Components Next.js 13+ RSC Zero-JS components on server Reduced bundle size
Progressive Hydration Load JS incrementally Prioritize visible/interactive parts Better perceived performance

Example: Streaming SSR with Suspense boundaries

// server.js - Node.js streaming SSR
import { renderToPipeableStream } from 'react-dom/server';
import App from './App';

app.get('/', (req, res) => {
  res.setHeader('Content-Type', 'text/html');

  const { pipe, abort } = renderToPipeableStream(<App />, {
    bootstrapScripts: ['/main.js'],
    onShellReady() {
      // Send initial shell immediately
      res.statusCode = 200;
      pipe(res);
    },
    onShellError(error) {
      res.statusCode = 500;
      res.send('<h1>Server Error</h1>');
    },
    onError(error) {
      console.error('Streaming error:', error);
    },
  });

  setTimeout(abort, 10000); // Abort after 10s
});

// App.tsx - Multiple Suspense boundaries
function App() {
  return (
    <html>
      <head>
        <title>Streaming SSR</title>
      </head>
      <body>
        <nav>Navigation (renders immediately)</nav>
        
        {/* High priority content */}
        <Suspense fallback={<HeroSkeleton />}>
          <Hero />
        </Suspense>

        {/* Stream independently */}
        <Suspense fallback={<ProductsSkeleton />}>
          <Products />
        </Suspense>

        {/* Lower priority, loads last */}
        <Suspense fallback={<CommentsSkeleton />}>
          <Comments />
        </Suspense>

        <footer>Footer (renders immediately)</footer>
      </body>
    </html>
  );
}

// Async component with data fetching
function Products() {
  const products = use(fetchProducts()); // React 19 use() hook

  return (
    <div>
      {products.map(p => <ProductCard key={p.id} product={p} />)}
    </div>
  );
}

// Next.js 13+ App Router with streaming
// app/page.tsx
import { Suspense } from 'react';

export default function Page() {
  return (
    <div>
      <h1>Dashboard</h1>
      
      {/* Fast content renders first */}
      <Suspense fallback={<Skeleton />}>
        <FastComponent />
      </Suspense>

      {/* Slow content streams later */}
      <Suspense fallback={<Skeleton />}>
        <SlowComponent />
      </Suspense>
    </div>
  );
}

// Server Component (no JS sent to client)
async function FastComponent() {
  const data = await fetch('https://api.example.com/fast').then(r => r.json());
  return <div>{data.title}</div>;
}

// Slow async component
async function SlowComponent() {
  await new Promise(resolve => setTimeout(resolve, 3000));
  const data = await fetch('https://api.example.com/slow').then(r => r.json());
  return <div>{data.content}</div>;
}

// Client Component for interactivity
'use client';

import { useState } from 'react';

function InteractiveWidget() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

// Streaming timeline visualization:
// Time 0ms:     HTML shell + nav + footer sent
// Time 100ms:   Hero content streams in
// Time 500ms:   Products stream in
// Time 2000ms:  Comments stream in
// User sees content progressively, not all at once

Rendering Strategy Comparison

Strategy Initial Load SEO Interactivity Best For
SSR (Next.js) Fast (pre-rendered) Excellent Hydration delay Content-heavy, SEO-critical
SSG (Static) Fastest (CDN) Excellent Hydration delay Blogs, docs, marketing
SPA (Vite) Slow (blank page) Poor Instant Web apps, dashboards
Streaming SSR Progressive Excellent Progressive Large pages, mixed content
ISR (Next.js) Fast (cached) Excellent Hydration delay E-commerce, dynamic content

6. Responsive Design Implementation Patterns

6.1 CSS Grid Flexbox Layout Systems

Property Syntax Description Use Case
CSS Grid display: grid 2D layout system for rows and columns Page layouts, complex grids
grid-template-columns repeat(auto-fit, minmax(250px, 1fr)) Responsive columns without media queries Card grids, galleries
Flexbox display: flex 1D layout for rows or columns Navigation, components
flex-wrap flex-wrap: wrap Items wrap to next line when space runs out Responsive lists
gap gap: 1rem Spacing between grid/flex items Consistent spacing
Grid Areas grid-template-areas Named template areas for semantic layouts Complex page structures

Example: CSS Grid and Flexbox responsive layouts

/* Responsive Grid - Auto-fit with minmax */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 2rem;
  padding: 1rem;
}

/* Responsive without media queries! */
.card {
  background: white;
  padding: 1.5rem;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* CSS Grid with named areas */
.page-layout {
  display: grid;
  grid-template-areas:
    "header header header"
    "sidebar main aside"
    "footer footer footer";
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: auto 1fr auto;
  gap: 1rem;
  min-height: 100vh;
}

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }

/* Responsive grid areas */
@media (max-width: 768px) {
  .page-layout {
    grid-template-areas:
      "header"
      "main"
      "sidebar"
      "aside"
      "footer";
    grid-template-columns: 1fr;
  }
}

/* Flexbox responsive navigation */
.nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem;
  padding: 1rem;
}

.nav-links {
  display: flex;
  gap: 2rem;
  flex-wrap: wrap;
}

/* Responsive flex direction */
.container {
  display: flex;
  flex-direction: row;
  gap: 2rem;
}

@media (max-width: 768px) {
  .container {
    flex-direction: column;
  }
}

/* Holy Grail Layout with Flexbox */
.holy-grail {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.holy-grail-body {
  display: flex;
  flex: 1;
}

.holy-grail-content {
  flex: 1;
}

.holy-grail-nav,
.holy-grail-ads {
  flex: 0 0 12em;
}

.holy-grail-nav {
  order: -1;
}

@media (max-width: 768px) {
  .holy-grail-body {
    flex-direction: column;
  }
  .holy-grail-nav,
  .holy-grail-ads {
    order: 0;
  }
}

/* Advanced Grid - Masonry-style layout */
.masonry {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  grid-auto-rows: 10px;
  gap: 1rem;
}

.masonry-item {
  grid-row-end: span 20; /* Adjust based on content height */
}

/* Subgrid (modern browsers) */
.parent-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

.child-grid {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: span 3;
}

6.2 Tailwind CSS Responsive Utilities

Breakpoint Syntax Min-Width Example
sm sm:text-lg 640px Small devices (tablets)
md md:flex-row 768px Medium devices (small laptops)
lg lg:grid-cols-3 1024px Large devices (desktops)
xl xl:container 1280px Extra large screens
2xl 2xl:px-8 1536px Ultra-wide screens
Custom theme.screens Configurable Custom breakpoints

Example: Tailwind CSS responsive design patterns

// tailwind.config.js - Custom breakpoints
module.exports = {
  theme: {
    screens: {
      'xs': '475px',
      'sm': '640px',
      'md': '768px',
      'lg': '1024px',
      'xl': '1280px',
      '2xl': '1536px',
      '3xl': '1920px',
    },
    extend: {
      spacing: {
        '128': '32rem',
      },
    },
  },
};

{/* Responsive grid with Tailwind */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
  <div className="bg-white p-4 rounded-lg shadow">Card 1</div>
  <div className="bg-white p-4 rounded-lg shadow">Card 2</div>
  <div className="bg-white p-4 rounded-lg shadow">Card 3</div>
</div>

{/* Mobile-first responsive typography */}
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl xl:text-6xl font-bold">
  Responsive Heading
</h1>

{/* Responsive flex direction */}
<div className="flex flex-col md:flex-row gap-4">
  <aside className="w-full md:w-1/4 bg-gray-100 p-4">Sidebar</aside>
  <main className="w-full md:w-3/4 p-4">Main Content</main>
</div>

{/* Responsive visibility */}
<button className="hidden md:block">Desktop Only</button>
<button className="md:hidden">Mobile Only</button>

{/* Responsive padding and margins */}
<div className="px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16">
  <div className="max-w-7xl mx-auto">
    Centered content with responsive padding
  </div>
</div>

{/* Responsive navigation */}
<nav className="bg-white shadow">
  <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
    <div className="flex justify-between h-16">
      <div className="flex">
        <div className="flex-shrink-0 flex items-center">
          Logo
        </div>
        <div className="hidden sm:ml-6 sm:flex sm:space-x-8">
          <a href="#" className="border-b-2 px-3 py-2">Home</a>
          <a href="#" className="px-3 py-2">About</a>
          <a href="#" className="px-3 py-2">Contact</a>
        </div>
      </div>
      <div className="sm:hidden">
        <button>Menu</button>
      </div>
    </div>
  </div>
</nav>

{/* Responsive container with breakpoint-specific max-widths */}
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
  {/* Container automatically adjusts max-width at each breakpoint */}
</div>

{/* Arbitrary values for custom responsive design */}
<div className="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4">
  Custom grid
</div>

{/* Responsive aspect ratios */}
<div className="aspect-video md:aspect-square lg:aspect-[16/9]">
  <img src="image.jpg" className="w-full h-full object-cover" />
</div>

{/* Dark mode + responsive */}
<div className="bg-white dark:bg-gray-800 p-4 sm:p-6 md:p-8">
  <h2 className="text-gray-900 dark:text-white text-lg sm:text-xl md:text-2xl">
    Responsive dark mode content
  </h2>
</div>

6.3 Container Queries CSS Modern NEW

Property Syntax Description Advantage
container-type container-type: inline-size Establishes containment context Component-level responsiveness
container-name container-name: card Names container for querying Multiple container contexts
@container @container (min-width: 400px) Query container dimensions, not viewport Truly reusable components
cqw/cqh width: 50cqw Container query width/height units Fluid component sizing
container shorthand container: card / inline-size Combined name and type declaration Cleaner syntax

Example: Container queries for component-based responsiveness

/* Container query setup */
.card-container {
  container-type: inline-size;
  container-name: card;
}

/* Component styles based on container width */
.card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1rem;
  background: white;
  border-radius: 8px;
}

/* When container is at least 400px wide */
@container card (min-width: 400px) {
  .card {
    flex-direction: row;
    align-items: center;
  }

  .card-image {
    width: 200px;
    height: 200px;
  }

  .card-content {
    flex: 1;
  }
}

/* When container is at least 600px wide */
@container card (min-width: 600px) {
  .card {
    padding: 2rem;
  }

  .card-title {
    font-size: 2rem;
  }
}

/* Product card with container queries */
.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 2rem;
}

.product-card {
  container-type: inline-size;
  background: white;
  border-radius: 12px;
  overflow: hidden;
}

.product-content {
  padding: 1rem;
}

.product-details {
  display: none; /* Hidden by default */
}

/* Show details when card is wide enough */
@container (min-width: 350px) {
  .product-details {
    display: block;
  }

  .product-button {
    width: 100%;
  }
}

@container (min-width: 500px) {
  .product-content {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
  }
}

/* Container query units */
.responsive-text {
  container-type: inline-size;
}

.responsive-text h1 {
  font-size: calc(5cqw + 1rem);
  /* Font size relative to container width */
}

/* Named containers with Tailwind (plugin) */
.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

@container sidebar (min-width: 300px) {
  .sidebar-nav {
    flex-direction: row;
  }
}

/* Nested containers */
.page {
  container-type: inline-size;
  container-name: page;
}

.section {
  container-type: inline-size;
  container-name: section;
}

/* Query specific container */
@container page (min-width: 1200px) {
  .page-header {
    display: flex;
  }
}

@container section (min-width: 400px) {
  .section-content {
    columns: 2;
  }
}

/* React component with container queries */
function ProductCard({ product }) {
  return (
    <div className="product-card">
      <img src={product.image} alt={product.name} />
      <div className="product-content">
        <h3>{product.name}</h3>
        <p className="product-price">${product.price}</p>
        <p className="product-details">{product.description}</p>
        <button className="product-button">Add to Cart</button>
      </div>
    </div>
  );
}

6.4 Intersection Observer Lazy Loading

Feature Syntax Description Use Case
IntersectionObserver new IntersectionObserver(callback) Observes element visibility in viewport Lazy loading, infinite scroll
threshold { threshold: 0.5 } Percentage of element visible to trigger Precise loading control
rootMargin { rootMargin: '100px' } Margin around viewport to trigger early Preload before visible
observe() observer.observe(element) Start observing an element Track visibility
unobserve() observer.unobserve(element) Stop observing an element Cleanup after loading
loading="lazy" <img loading="lazy"> Native browser lazy loading Simple image/iframe lazy load

Example: Intersection Observer for lazy loading

// Vanilla JS lazy loading images
const images = document.querySelectorAll('img[data-src]');

const imageObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.add('loaded');
      observer.unobserve(img);
    }
  });
}, {
  rootMargin: '50px', // Load 50px before entering viewport
  threshold: 0.1,
});

images.forEach(img => imageObserver.observe(img));

// React lazy loading component
import { useEffect, useRef, useState } from 'react';

function LazyImage({ src, alt, placeholder }) {
  const [isLoaded, setIsLoaded] = useState(false);
  const [isInView, setIsInView] = useState(false);
  const imgRef = useRef<HTMLImageElement>(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsInView(true);
          observer.disconnect();
        }
      },
      { rootMargin: '100px' }
    );

    if (imgRef.current) {
      observer.observe(imgRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <img
      ref={imgRef}
      src={isInView ? src : placeholder}
      alt={alt}
      onLoad={() => setIsLoaded(true)}
      style={{
        opacity: isLoaded ? 1 : 0.5,
        transition: 'opacity 0.3s',
      }}
    />
  );
}

// Infinite scroll implementation
function InfiniteScroll() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const loaderRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting && hasMore) {
          loadMore();
        }
      },
      { threshold: 1.0 }
    );

    if (loaderRef.current) {
      observer.observe(loaderRef.current);
    }

    return () => observer.disconnect();
  }, [hasMore, page]);

  const loadMore = async () => {
    const newItems = await fetchItems(page);
    setItems(prev => [...prev, ...newItems]);
    setPage(prev => prev + 1);
    setHasMore(newItems.length > 0);
  };

  return (
    <div>
      {items.map(item => <div key={item.id}>{item.name}</div>)}
      <div ref={loaderRef}>{hasMore && 'Loading...'}</div>
    </div>
  );
}

// Custom hook for intersection observer
function useIntersectionObserver(
  ref: React.RefObject<Element>,
  options: IntersectionObserverInit = {}
) {
  const [isIntersecting, setIsIntersecting] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      setIsIntersecting(entry.isIntersecting);
    }, options);

    if (ref.current) {
      observer.observe(ref.current);
    }

    return () => observer.disconnect();
  }, [ref, options]);

  return isIntersecting;
}

// Usage
function AnimatedSection() {
  const ref = useRef(null);
  const isVisible = useIntersectionObserver(ref, { threshold: 0.5 });

  return (
    <div
      ref={ref}
      style={{
        opacity: isVisible ? 1 : 0,
        transform: isVisible ? 'translateY(0)' : 'translateY(50px)',
        transition: 'all 0.6s ease-out',
      }}
    >
      Content fades in when 50% visible
    </div>
  );
}

// Native lazy loading (simpler approach)
<img
  src="image.jpg"
  alt="Description"
  loading="lazy"
  decoding="async"
/>

<iframe
  src="https://example.com"
  loading="lazy"
></iframe>

// Background image lazy loading
function LazyBackground({ imageUrl, children }) {
  const [isLoaded, setIsLoaded] = useState(false);
  const divRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        setIsLoaded(true);
        observer.disconnect();
      }
    });

    if (divRef.current) {
      observer.observe(divRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <div
      ref={divRef}
      style={{
        backgroundImage: isLoaded ? `url(${imageUrl})` : 'none',
        backgroundColor: '#f0f0f0',
        backgroundSize: 'cover',
      }}
    >
      {children}
    </div>
  );
}

6.5 Mobile-First Breakpoint Strategy

Breakpoint Device Approach Best Practice
Base (mobile) 320px - 639px Default styles, no media query Single column, stack elements
sm (tablet) 640px+ @media (min-width: 640px) 2 columns, larger text
md (laptop) 768px+ @media (min-width: 768px) Sidebar layouts, 3 columns
lg (desktop) 1024px+ @media (min-width: 1024px) Multi-column grids, hover states
xl (wide) 1280px+ @media (min-width: 1280px) Max content width, more spacing
Touch-first All mobile 44px touch targets, no hover Accessibility, usability

Example: Mobile-first responsive design

/* Mobile-first CSS approach */

/* Base styles (mobile, no media query) */
.container {
  width: 100%;
  padding: 1rem;
}

.grid {
  display: grid;
  grid-template-columns: 1fr; /* Single column on mobile */
  gap: 1rem;
}

.nav {
  flex-direction: column;
}

.button {
  min-height: 44px; /* Touch-friendly target */
  width: 100%;
  font-size: 16px; /* Prevents zoom on iOS */
}

/* Tablet (640px+) */
@media (min-width: 640px) {
  .container {
    padding: 2rem;
  }

  .grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .button {
    width: auto;
    min-width: 120px;
  }
}

/* Laptop (768px+) */
@media (min-width: 768px) {
  .container {
    max-width: 768px;
    margin: 0 auto;
  }

  .grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 2rem;
  }

  .nav {
    flex-direction: row;
    justify-content: space-between;
  }

  /* Show hover states only on larger screens */
  .button:hover {
    background-color: #0056b3;
  }
}

/* Desktop (1024px+) */
@media (min-width: 1024px) {
  .container {
    max-width: 1024px;
  }

  .grid {
    grid-template-columns: repeat(4, 1fr);
  }

  .sidebar {
    display: block; /* Show sidebar on desktop */
  }
}

/* Wide screens (1280px+) */
@media (min-width: 1280px) {
  .container {
    max-width: 1280px;
    padding: 3rem;
  }
}

/* React mobile-first component */
function ResponsiveCard() {
  return (
    <div className="
      /* Mobile base */
      flex flex-col
      p-4
      w-full
      
      /* Tablet */
      sm:flex-row
      sm:p-6
      
      /* Laptop */
      md:max-w-2xl
      md:mx-auto
      
      /* Desktop */
      lg:max-w-4xl
      lg:p-8
    ">
      <div className="
        /* Mobile: full width image */
        w-full
        h-48
        
        /* Tablet: side image */
        sm:w-1/3
        sm:h-auto
        
        /* Laptop: larger */
        md:w-1/4
      ">
        <img src="image.jpg" alt="Card" className="w-full h-full object-cover" />
      </div>
      
      <div className="
        /* Mobile: full width content */
        w-full
        mt-4
        
        /* Tablet: beside image */
        sm:w-2/3
        sm:mt-0
        sm:pl-6
        
        /* Laptop: more space */
        md:w-3/4
        md:pl-8
      ">
        <h2 className="text-xl sm:text-2xl md:text-3xl lg:text-4xl">
          Responsive Title
        </h2>
        <p className="mt-2 text-sm sm:text-base md:text-lg">
          Description text
        </p>
      </div>
    </div>
  );
}

/* Mobile-first typography scale */
:root {
  /* Mobile base sizes */
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
}

@media (min-width: 768px) {
  :root {
    /* Larger text on desktop */
    --text-base: 1.125rem;
    --text-lg: 1.25rem;
    --text-xl: 1.5rem;
    --text-2xl: 2rem;
  }
}

/* Touch-first interactions */
.interactive {
  /* Minimum touch target size */
  min-width: 44px;
  min-height: 44px;
  
  /* No hover effects on touch devices */
  -webkit-tap-highlight-color: transparent;
}

@media (hover: hover) {
  /* Only add hover effects on devices that support hover */
  .interactive:hover {
    background-color: #f0f0f0;
  }
}

/* Mobile-first navigation */
.mobile-nav {
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: white;
  transform: translateX(-100%);
  transition: transform 0.3s;
}

.mobile-nav.open {
  transform: translateX(0);
}

@media (min-width: 768px) {
  .mobile-nav {
    position: static;
    flex-direction: row;
    width: auto;
    height: auto;
    transform: none;
  }
}

6.6 Adaptive Loading Network Conditions

API Property Description Use Case
Network Information navigator.connection Access network connection info Adapt to connection quality
effectiveType connection.effectiveType '4g', '3g', '2g', 'slow-2g' Load different assets
saveData connection.saveData User enabled data saver mode Reduce data usage
deviceMemory navigator.deviceMemory Device RAM in GB Load lighter alternatives
hardwareConcurrency navigator.hardwareConcurrency Number of CPU cores Adjust processing tasks
Adaptive Loading react-adaptive-hooks React hooks for adaptive loading Component-level adaptation

Example: Adaptive loading based on network and device

// Check network connection
function getNetworkInfo() {
  const connection = navigator.connection 
    || navigator.mozConnection 
    || navigator.webkitConnection;

  if (!connection) {
    return { effectiveType: '4g', saveData: false };
  }

  return {
    effectiveType: connection.effectiveType,
    saveData: connection.saveData,
    downlink: connection.downlink,
    rtt: connection.rtt,
  };
}

// Adaptive image loading
function AdaptiveImage({ src, alt }) {
  const network = getNetworkInfo();
  
  // Load different image quality based on connection
  const imageSrc = network.effectiveType === '4g' && !network.saveData
    ? src.high
    : network.effectiveType === '3g'
    ? src.medium
    : src.low;

  return <img src={imageSrc} alt={alt} loading="lazy" />;
}

// React hook for network status
import { useEffect, useState } from 'react';

function useNetworkStatus() {
  const [status, setStatus] = useState({
    online: navigator.onLine,
    effectiveType: '4g',
    saveData: false,
  });

  useEffect(() => {
    const connection = navigator.connection;

    const updateNetworkStatus = () => {
      setStatus({
        online: navigator.onLine,
        effectiveType: connection?.effectiveType || '4g',
        saveData: connection?.saveData || false,
      });
    };

    updateNetworkStatus();

    window.addEventListener('online', updateNetworkStatus);
    window.addEventListener('offline', updateNetworkStatus);
    connection?.addEventListener('change', updateNetworkStatus);

    return () => {
      window.removeEventListener('online', updateNetworkStatus);
      window.removeEventListener('offline', updateNetworkStatus);
      connection?.removeEventListener('change', updateNetworkStatus);
    };
  }, []);

  return status;
}

// Usage
function VideoPlayer({ videoUrl }) {
  const { effectiveType, saveData, online } = useNetworkStatus();

  if (!online) {
    return <div>You are offline</div>;
  }

  // Adapt video quality
  const quality = saveData || effectiveType === '2g' ? 'low'
    : effectiveType === '3g' ? 'medium'
    : 'high';

  return (
    <video src={videoUrl[quality]} controls autoPlay={effectiveType === '4g'} />
  );
}

// Device capability detection
function useDeviceCapabilities() {
  return {
    memory: navigator.deviceMemory || 4,
    cores: navigator.hardwareConcurrency || 2,
    platform: navigator.platform,
  };
}

// Adaptive component loading
function Dashboard() {
  const { memory, cores } = useDeviceCapabilities();
  const { effectiveType, saveData } = useNetworkStatus();

  // Load heavy components only on capable devices with good connection
  const shouldLoadHeavyFeatures = 
    memory >= 4 && 
    cores >= 4 && 
    effectiveType === '4g' && 
    !saveData;

  return (
    <div>
      <BasicDashboard />
      {shouldLoadHeavyFeatures && (
        <Suspense fallback={<Skeleton />}>
          <HeavyChart />
          <RealTimeData />
        </Suspense>
      )}
    </div>
  );
}

// Adaptive image component with multiple strategies
function SmartImage({ src, alt, sizes }) {
  const { effectiveType, saveData } = useNetworkStatus();
  const { memory } = useDeviceCapabilities();

  // Preload critical images on fast connections
  useEffect(() => {
    if (effectiveType === '4g' && !saveData) {
      const link = document.createElement('link');
      link.rel = 'preload';
      link.as = 'image';
      link.href = src;
      document.head.appendChild(link);
    }
  }, [src, effectiveType, saveData]);

  // Format based on device capability
  const format = memory >= 4 ? 'webp' : 'jpg';

  return (
    <picture>
      {!saveData && effectiveType === '4g' && (
        <source srcSet={`${src}.${format} 2x`} type={`image/${format}`} />
      )}
      <source srcSet={`${src}.${format}`} type={`image/${format}`} />
      <img src={`${src}.jpg`} alt={alt} loading="lazy" />
    </picture>
  );
}

// react-adaptive-hooks library
import {
  useNetworkStatus,
  useSaveData,
  useHardwareConcurrency,
  useMemoryStatus,
} from 'react-adaptive-hooks';

function AdaptiveApp() {
  const { effectiveConnectionType } = useNetworkStatus();
  const { saveData } = useSaveData();
  const { numberOfLogicalProcessors } = useHardwareConcurrency();
  const { deviceMemory } = useMemoryStatus();

  const isLowEndDevice = deviceMemory < 4 || numberOfLogicalProcessors < 4;
  const isSlowNetwork = effectiveConnectionType === '2g' || effectiveConnectionType === '3g';

  return (
    <div>
      {isLowEndDevice || isSlowNetwork || saveData ? (
        <LightweightComponent />
      ) : (
        <FeatureRichComponent />
      )}
    </div>
  );
}

// Adaptive font loading
if (navigator.connection?.effectiveType === '4g' && !navigator.connection?.saveData) {
  // Load custom fonts
  document.fonts.load('16px CustomFont').then(() => {
    document.body.classList.add('fonts-loaded');
  });
} else {
  // Use system fonts
  document.body.style.fontFamily = 'system-ui, sans-serif';
}

Responsive Design Best Practices

  • Mobile-First - Start with mobile styles, enhance for larger screens with min-width media queries
  • CSS Grid + Flexbox - Use Grid for 2D layouts, Flexbox for 1D components, leverage auto-fit/minmax for responsiveness
  • Container Queries - Component-based responsiveness, truly reusable components independent of viewport
  • Tailwind Utilities - Rapid responsive development with sm/md/lg/xl breakpoint prefixes
  • Lazy Loading - Use Intersection Observer for images, components, infinite scroll; native loading="lazy" for simple cases
  • Adaptive Loading - Detect network conditions (4g/3g/2g), save-data mode, device memory to serve appropriate assets
  • Touch-First - Minimum 44px touch targets, avoid hover-only interactions, use pointer events for unified input handling

7. Accessibility Implementation Best Practices

7.1 WCAG 2.1 AA Semantic HTML5

Element Purpose WCAG Guideline Example
<header> Page/section header Info and Relationships (1.3.1) Site banner, article header
<nav> Navigation links Info and Relationships (1.3.1) Main menu, breadcrumbs
<main> Primary content Info and Relationships (1.3.1) One per page, unique content
<article> Self-contained content Info and Relationships (1.3.1) Blog post, news article
<aside> Tangentially related content Info and Relationships (1.3.1) Sidebar, related links
<footer> Page/section footer Info and Relationships (1.3.1) Copyright, contact info
Heading hierarchy <h1> to <h6> in order Info and Relationships (1.3.1) Don't skip levels (h2 → h4)

Example: WCAG 2.1 AA compliant semantic HTML structure

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Accessible Web Page</title>
</head>
<body>
  <!-- Skip to main content link -->
  <a href="#main-content" class="skip-link">Skip to main content</a>

  <!-- Page header with site branding -->
  <header role="banner">
    <h1>Site Name</h1>
    <p>Tagline describing the site</p>
  </header>

  <!-- Main navigation -->
  <nav aria-label="Main navigation">
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/about">About</a></li>
      <li><a href="/contact">Contact</a></li>
    </ul>
  </nav>

  <!-- Main content area -->
  <main id="main-content">
    <h2>Page Title</h2>
    
    <!-- Article section -->
    <article>
      <h3>Article Heading</h3>
      <p>Article content with <a href="#">meaningful link text</a>.</p>
      
      <section>
        <h4>Subsection Heading</h4>
        <p>Subsection content.</p>
      </section>
    </article>

    <!-- Accessible form -->
    <form action="/submit" method="post">
      <fieldset>
        <legend>Contact Information</legend>
        
        <label for="name">Name</label>
        <input type="text" id="name" name="name" required aria-required="true">
        
        <label for="email">Email</label>
        <input 
          type="email" 
          id="email" 
          name="email" 
          required 
          aria-required="true"
          aria-describedby="email-hint"
        >
        <span id="email-hint" class="hint">We'll never share your email.</span>
      </fieldset>
      
      <button type="submit">Submit</button>
    </form>

    <!-- Accessible table -->
    <table>
      <caption>Monthly Sales Data</caption>
      <thead>
        <tr>
          <th scope="col">Month</th>
          <th scope="col">Revenue</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th scope="row">January</th>
          <td>$10,000</td>
        </tr>
      </tbody>
    </table>

    <!-- Accessible image -->
    <figure>
      <img 
        src="chart.png" 
        alt="Bar chart showing 25% increase in sales over Q1"
      >
      <figcaption>Sales growth in Q1 2025</figcaption>
    </figure>

    <!-- Decorative image (empty alt) -->
    <img src="decorative-line.png" alt="" role="presentation">
  </main>

  <!-- Sidebar content -->
  <aside aria-label="Related content">
    <h2>Related Articles</h2>
    <ul>
      <li><a href="/article1">Article 1</a></li>
      <li><a href="/article2">Article 2</a></li>
    </ul>
  </aside>

  <!-- Page footer -->
  <footer role="contentinfo">
    <p>&copy; 2025 Company Name</p>
    <nav aria-label="Footer navigation">
      <a href="/privacy">Privacy Policy</a>
      <a href="/terms">Terms of Service</a>
    </nav>
  </footer>
</body>
</html>

<!-- CSS for skip link -->
<style>
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: #000;
  color: #fff;
  padding: 8px;
  z-index: 100;
}

.skip-link:focus {
  top: 0;
}
</style>

7.2 ARIA Labels Roles Screen Readers

ARIA Attribute Purpose Example Use Case
role Defines element's purpose role="button" Non-semantic elements as interactive
aria-label Provides accessible name aria-label="Close dialog" Icon buttons without text
aria-labelledby References element for label aria-labelledby="heading-id" Associate region with heading
aria-describedby Additional description aria-describedby="help-text" Form field hints, tooltips
aria-live Announces dynamic content aria-live="polite" Status messages, notifications
aria-hidden Hides from screen readers aria-hidden="true" Decorative icons, duplicates
aria-expanded Expandable state aria-expanded="false" Accordions, dropdowns
aria-current Current item in set aria-current="page" Active navigation link

Example: ARIA attributes for accessible components

// Icon button with aria-label
<button aria-label="Close dialog">
  <svg aria-hidden="true">...</svg>
</button>

// Search form with aria-labelledby
<form role="search" aria-labelledby="search-heading">
  <h2 id="search-heading">Search Products</h2>
  <input type="search" aria-label="Search query">
  <button type="submit">Search</button>
</form>

// Form field with aria-describedby
<label for="password">Password</label>
<input 
  type="password" 
  id="password"
  aria-describedby="password-requirements"
  aria-invalid="false"
>
<div id="password-requirements">
  Must be at least 8 characters
</div>

// Accordion with aria-expanded
function Accordion() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button
        aria-expanded={isOpen}
        aria-controls="accordion-content"
        onClick={() => setIsOpen(!isOpen)}
      >
        Show Details
      </button>
      {isOpen && (
        <div id="accordion-content" role="region">
          Accordion content
        </div>
      )}
    </div>
  );
}

// Live region for notifications
<div 
  role="status" 
  aria-live="polite" 
  aria-atomic="true"
  className="sr-only"
>
  {statusMessage}
</div>

// Alert for errors
<div role="alert" aria-live="assertive">
  {errorMessage}
</div>

// Modal dialog
function Modal({ isOpen, onClose, title, children }) {
  return (
    <div
      role="dialog"
      aria-modal="true"
      aria-labelledby="dialog-title"
      hidden={!isOpen}
    >
      <h2 id="dialog-title">{title}</h2>
      <div>{children}</div>
      <button onClick={onClose} aria-label="Close dialog">
        <span aria-hidden="true">&times;</span>
      </button>
    </div>
  );
}

// Navigation with aria-current
<nav aria-label="Main navigation">
  <a href="/" aria-current="page">Home</a>
  <a href="/about">About</a>
  <a href="/contact">Contact</a>
</nav>

// Tabs with ARIA
function Tabs() {
  const [activeTab, setActiveTab] = useState(0);

  return (
    <div>
      <div role="tablist" aria-label="Content tabs">
        <button
          role="tab"
          aria-selected={activeTab === 0}
          aria-controls="panel-0"
          id="tab-0"
          onClick={() => setActiveTab(0)}
        >
          Tab 1
        </button>
        <button
          role="tab"
          aria-selected={activeTab === 1}
          aria-controls="panel-1"
          id="tab-1"
          onClick={() => setActiveTab(1)}
        >
          Tab 2
        </button>
      </div>
      
      <div
        role="tabpanel"
        id="panel-0"
        aria-labelledby="tab-0"
        hidden={activeTab !== 0}
      >
        Panel 1 content
      </div>
      <div
        role="tabpanel"
        id="panel-1"
        aria-labelledby="tab-1"
        hidden={activeTab !== 1}
      >
        Panel 2 content
      </div>
    </div>
  );
}

// Loading state with aria-busy
<div aria-busy={isLoading} aria-live="polite">
  {isLoading ? 'Loading...' : content}
</div>

// Visually hidden but screen-reader accessible
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

<span class="sr-only">Additional context for screen readers</span>

7.3 Focus Management roving tabindex

Technique Implementation Description Use Case
tabindex="0" Includes in tab order Makes non-interactive elements focusable Custom widgets
tabindex="-1" Programmatically focusable Not in tab order, focus via JS Skip links, roving tabindex
Roving tabindex One item tabindex="0" Single tab stop, arrow keys navigate Toolbars, menus, grids
Focus trap Constrain focus within Prevent focus leaving modal/dialog Modals, drawers
Focus visible :focus-visible Show focus only for keyboard users Better UX than :focus
Focus restoration Return focus after action Focus trigger after closing modal Modal dialogs

Example: Focus management and roving tabindex

// Roving tabindex for toolbar
function Toolbar({ items }) {
  const [focusedIndex, setFocusedIndex] = useState(0);
  const itemRefs = useRef([]);

  const handleKeyDown = (e, index) => {
    let newIndex = index;

    switch (e.key) {
      case 'ArrowRight':
        newIndex = (index + 1) % items.length;
        break;
      case 'ArrowLeft':
        newIndex = (index - 1 + items.length) % items.length;
        break;
      case 'Home':
        newIndex = 0;
        break;
      case 'End':
        newIndex = items.length - 1;
        break;
      default:
        return;
    }

    e.preventDefault();
    setFocusedIndex(newIndex);
    itemRefs.current[newIndex]?.focus();
  };

  return (
    <div role="toolbar" aria-label="Text formatting">
      {items.map((item, index) => (
        <button
          key={item.id}
          ref={el => itemRefs.current[index] = el}
          tabIndex={index === focusedIndex ? 0 : -1}
          onKeyDown={(e) => handleKeyDown(e, index)}
          onFocus={() => setFocusedIndex(index)}
          aria-label={item.label}
        >
          {item.icon}
        </button>
      ))}
    </div>
  );
}

// Focus trap for modal
import { useEffect, useRef } from 'react';

function FocusTrap({ children }) {
  const trapRef = useRef(null);

  useEffect(() => {
    const trap = trapRef.current;
    if (!trap) return;

    const focusableElements = trap.querySelectorAll(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    );
    const firstElement = focusableElements[0];
    const lastElement = focusableElements[focusableElements.length - 1];

    const handleTabKey = (e) => {
      if (e.key !== 'Tab') return;

      if (e.shiftKey) {
        if (document.activeElement === firstElement) {
          e.preventDefault();
          lastElement.focus();
        }
      } else {
        if (document.activeElement === lastElement) {
          e.preventDefault();
          firstElement.focus();
        }
      }
    };

    trap.addEventListener('keydown', handleTabKey);
    firstElement?.focus();

    return () => trap.removeEventListener('keydown', handleTabKey);
  }, []);

  return <div ref={trapRef}>{children}</div>;
}

// Modal with focus management
function Modal({ isOpen, onClose, children }) {
  const previousFocus = useRef(null);
  const modalRef = useRef(null);

  useEffect(() => {
    if (isOpen) {
      // Save current focus
      previousFocus.current = document.activeElement;
      
      // Focus modal
      modalRef.current?.focus();
    } else {
      // Restore focus when closed
      previousFocus.current?.focus();
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <div
      ref={modalRef}
      role="dialog"
      aria-modal="true"
      tabIndex={-1}
    >
      <FocusTrap>
        {children}
        <button onClick={onClose}>Close</button>
      </FocusTrap>
    </div>
  );
}

// Focus-visible CSS
button:focus {
  outline: none; /* Remove default */
}

button:focus-visible {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
}

// Custom hook for roving tabindex
function useRovingTabIndex(size) {
  const [currentIndex, setCurrentIndex] = useState(0);

  const handleKeyDown = useCallback((e) => {
    switch (e.key) {
      case 'ArrowRight':
      case 'ArrowDown':
        e.preventDefault();
        setCurrentIndex((prev) => (prev + 1) % size);
        break;
      case 'ArrowLeft':
      case 'ArrowUp':
        e.preventDefault();
        setCurrentIndex((prev) => (prev - 1 + size) % size);
        break;
      case 'Home':
        e.preventDefault();
        setCurrentIndex(0);
        break;
      case 'End':
        e.preventDefault();
        setCurrentIndex(size - 1);
        break;
    }
  }, [size]);

  return { currentIndex, setCurrentIndex, handleKeyDown };
}

// Usage
function Menu() {
  const items = ['Item 1', 'Item 2', 'Item 3'];
  const { currentIndex, handleKeyDown } = useRovingTabIndex(items.length);

  return (
    <div role="menu">
      {items.map((item, index) => (
        <div
          key={index}
          role="menuitem"
          tabIndex={index === currentIndex ? 0 : -1}
          onKeyDown={handleKeyDown}
        >
          {item}
        </div>
      ))}
    </div>
  );
}

// Skip to main content
<a href="#main-content" className="skip-link">
  Skip to main content
</a>

<main id="main-content" tabIndex={-1}>
  {/* Main content */}
</main>

7.4 Color Contrast Checker WebAIM

Level Contrast Ratio Requirement Use Case
AA Normal Text 4.5:1 Minimum for body text <18px or <14px bold
AA Large Text 3:1 Large or bold text >=18px or >=14px bold
AAA Normal Text 7:1 Enhanced contrast Higher accessibility
AAA Large Text 4.5:1 Enhanced for large text Better readability
UI Components 3:1 Form controls, icons Interactive elements
Focus Indicators 3:1 Focus state contrast Keyboard navigation

Example: Color contrast compliance and testing

/* WCAG AA Compliant Colors */

/* Good contrast examples (AA compliant) */
:root {
  --text-dark: #1a1a1a;      /* On white: 16.1:1 (AAA) */
  --text-medium: #595959;     /* On white: 7:1 (AAA) */
  --text-light: #757575;      /* On white: 4.6:1 (AA) */
  
  --primary: #0066cc;         /* On white: 4.7:1 (AA) */
  --primary-dark: #004080;    /* On white: 8.6:1 (AAA) */
  
  --success: #008000;         /* On white: 4.5:1 (AA) */
  --error: #c00000;           /* On white: 7.3:1 (AAA) */
  --warning: #c68400;         /* On white: 4.5:1 (AA) */
  
  /* Dark mode */
  --bg-dark: #1a1a1a;
  --text-dark-mode: #e6e6e6;  /* On dark: 11.6:1 (AAA) */
}

/* Bad contrast examples (WCAG fails) */
.bad-contrast {
  color: #999;                /* On white: 2.8:1 (FAIL) */
  background: #fff;
}

/* Good contrast example */
.good-contrast {
  color: #595959;             /* On white: 7:1 (AAA) */
  background: #fff;
}

/* Button with sufficient contrast */
.button {
  background: #0066cc;        /* Background color */
  color: #ffffff;             /* Text: 4.7:1 (AA) */
  border: 2px solid #004080;  /* Border: 8.6:1 (AAA) */
}

.button:focus {
  outline: 2px solid #0066cc; /* 3:1 with background (AA) */
  outline-offset: 2px;
}

/* Link colors with sufficient contrast */
a {
  color: #0066cc;             /* 4.7:1 on white (AA) */
  text-decoration: underline; /* Don't rely on color alone */
}

a:visited {
  color: #551a8b;             /* 6.4:1 on white (AA) */
}

/* Testing contrast in JavaScript */
function getContrast(foreground, background) {
  const getLuminance = (color) => {
    const rgb = color.match(/\d+/g).map(Number);
    const [r, g, b] = rgb.map(c => {
      c = c / 255;
      return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
    });
    return 0.2126 * r + 0.7152 * g + 0.0722 * b;
  };

  const l1 = getLuminance(foreground);
  const l2 = getLuminance(background);
  const lighter = Math.max(l1, l2);
  const darker = Math.min(l1, l2);

  return (lighter + 0.05) / (darker + 0.05);
}

// Check if contrast meets WCAG AA
const contrastRatio = getContrast('rgb(0, 102, 204)', 'rgb(255, 255, 255)');
const meetsAA = contrastRatio >= 4.5; // true

// React component for contrast checking
function ContrastChecker({ foreground, background }) {
  const ratio = getContrast(foreground, background);
  const meetsAA = ratio >= 4.5;
  const meetsAAA = ratio >= 7;

  return (
    <div>
      <div style={{ color: foreground, background }}>
        Sample Text
      </div>
      <p>Contrast Ratio: {ratio.toFixed(2)}:1</p>
      <p>WCAG AA: {meetsAA ? '✓ Pass' : '✗ Fail'}</p>
      <p>WCAG AAA: {meetsAAA ? '✓ Pass' : '✗ Fail'}</p>
    </div>
  );
}

/* Dark mode with proper contrast */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1a1a1a;
    --text: #e6e6e6;          /* 11.6:1 (AAA) */
    --text-muted: #b3b3b3;    /* 5.7:1 (AA) */
    --primary: #4d94ff;       /* 5.1:1 on dark (AA) */
  }

  body {
    background: var(--bg);
    color: var(--text);
  }
}

/* Don't rely on color alone - use icons/patterns */
.status-success {
  color: #008000;
  /* Add icon for non-color indicator */
}

.status-success::before {
  content: '✓';
  margin-right: 0.5rem;
}

.status-error {
  color: #c00000;
}

.status-error::before {
  content: '✗';
  margin-right: 0.5rem;
}

/* WebAIM Contrast Checker Tools */
// Online: https://webaim.org/resources/contrastchecker/
// Browser DevTools: Chrome/Edge Lighthouse, Firefox Accessibility Inspector
// VS Code Extensions: webhint, axe Accessibility Linter
// npm packages: axe-core, pa11y

7.5 Keyboard Navigation Testing

Key Action Expected Behavior Component
Tab Move forward Focus next interactive element All focusable elements
Shift + Tab Move backward Focus previous interactive element All focusable elements
Enter Activate Trigger button/link action Buttons, links
Space Activate Toggle checkbox, press button Buttons, checkboxes
Arrow keys Navigate within Move between items in group Radio, select, tabs, menus
Escape Close/Cancel Close modal, dismiss menu Modals, menus, tooltips
Home/End Jump to start/end First/last item in list Lists, select, inputs

Example: Keyboard navigation implementation

// Keyboard-accessible dropdown
function Dropdown({ items, label }) {
  const [isOpen, setIsOpen] = useState(false);
  const [selectedIndex, setSelectedIndex] = useState(-1);
  const buttonRef = useRef(null);
  const itemRefs = useRef([]);

  const handleKeyDown = (e) => {
    switch (e.key) {
      case 'Enter':
      case ' ':
        e.preventDefault();
        setIsOpen(!isOpen);
        break;
      case 'Escape':
        setIsOpen(false);
        buttonRef.current?.focus();
        break;
      case 'ArrowDown':
        e.preventDefault();
        if (!isOpen) {
          setIsOpen(true);
        } else {
          const next = (selectedIndex + 1) % items.length;
          setSelectedIndex(next);
          itemRefs.current[next]?.focus();
        }
        break;
      case 'ArrowUp':
        e.preventDefault();
        if (isOpen) {
          const prev = (selectedIndex - 1 + items.length) % items.length;
          setSelectedIndex(prev);
          itemRefs.current[prev]?.focus();
        }
        break;
      case 'Home':
        e.preventDefault();
        setSelectedIndex(0);
        itemRefs.current[0]?.focus();
        break;
      case 'End':
        e.preventDefault();
        setSelectedIndex(items.length - 1);
        itemRefs.current[items.length - 1]?.focus();
        break;
    }
  };

  return (
    <div>
      <button
        ref={buttonRef}
        aria-haspopup="listbox"
        aria-expanded={isOpen}
        onKeyDown={handleKeyDown}
        onClick={() => setIsOpen(!isOpen)}
      >
        {label}
      </button>
      {isOpen && (
        <ul role="listbox">
          {items.map((item, index) => (
            <li
              key={item.id}
              ref={el => itemRefs.current[index] = el}
              role="option"
              tabIndex={-1}
              aria-selected={index === selectedIndex}
              onKeyDown={handleKeyDown}
            >
              {item.label}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

// Keyboard navigation for tabs
function Tabs({ tabs }) {
  const [activeIndex, setActiveIndex] = useState(0);
  const tabRefs = useRef([]);

  const handleTabKeyDown = (e, index) => {
    let newIndex = index;

    switch (e.key) {
      case 'ArrowRight':
        newIndex = (index + 1) % tabs.length;
        break;
      case 'ArrowLeft':
        newIndex = (index - 1 + tabs.length) % tabs.length;
        break;
      case 'Home':
        newIndex = 0;
        break;
      case 'End':
        newIndex = tabs.length - 1;
        break;
      default:
        return;
    }

    e.preventDefault();
    setActiveIndex(newIndex);
    tabRefs.current[newIndex]?.focus();
  };

  return (
    <div>
      <div role="tablist">
        {tabs.map((tab, index) => (
          <button
            key={tab.id}
            ref={el => tabRefs.current[index] = el}
            role="tab"
            tabIndex={index === activeIndex ? 0 : -1}
            aria-selected={index === activeIndex}
            aria-controls={`panel-${index}`}
            onKeyDown={(e) => handleTabKeyDown(e, index)}
            onClick={() => setActiveIndex(index)}
          >
            {tab.label}
          </button>
        ))}
      </div>
      {tabs.map((tab, index) => (
        <div
          key={tab.id}
          role="tabpanel"
          id={`panel-${index}`}
          hidden={index !== activeIndex}
          tabIndex={0}
        >
          {tab.content}
        </div>
      ))}
    </div>
  );
}

// Modal with keyboard handling
function Modal({ isOpen, onClose, children }) {
  useEffect(() => {
    const handleEscape = (e) => {
      if (e.key === 'Escape' && isOpen) {
        onClose();
      }
    };

    document.addEventListener('keydown', handleEscape);
    return () => document.removeEventListener('keydown', handleEscape);
  }, [isOpen, onClose]);

  if (!isOpen) return null;

  return (
    <div role="dialog" aria-modal="true">
      {children}
      <button onClick={onClose}>Close (Esc)</button>
    </div>
  );
}

// Custom checkbox with keyboard support
function Checkbox({ checked, onChange, label }) {
  const handleKeyDown = (e) => {
    if (e.key === ' ') {
      e.preventDefault();
      onChange(!checked);
    }
  };

  return (
    <div
      role="checkbox"
      aria-checked={checked}
      tabIndex={0}
      onKeyDown={handleKeyDown}
      onClick={() => onChange(!checked)}
    >
      {label}
    </div>
  );
}

// Keyboard testing checklist
const keyboardTests = [
  'Can navigate all interactive elements with Tab',
  'Can operate all functionality with keyboard only',
  'Focus indicator is clearly visible',
  'Tab order follows logical sequence',
  'No keyboard traps (can escape all contexts)',
  'Arrow keys work in composite widgets',
  'Enter/Space activate buttons',
  'Escape closes modals and menus',
  'Can skip repetitive content (skip links)',
  'Custom widgets follow ARIA keyboard patterns',
];

// Testing script
function testKeyboardNavigation() {
  console.log('Keyboard Navigation Test');
  
  // Get all focusable elements
  const focusable = document.querySelectorAll(
    'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  
  console.log(`Found ${focusable.length} focusable elements`);
  
  // Check tab order
  focusable.forEach((el, i) => {
    console.log(`${i + 1}. ${el.tagName} - ${el.textContent?.trim()}`);
  });
}

7.6 axe-core Automated Testing

Tool Usage Description Integration
axe-core JavaScript library Automated accessibility testing engine Runtime, CI/CD
@axe-core/react React integration Logs violations in development Development mode
jest-axe Jest matcher Accessibility assertions in tests Unit/integration tests
cypress-axe Cypress plugin E2E accessibility testing End-to-end tests
axe DevTools Browser extension Manual testing in DevTools Development, QA
pa11y CLI tool Automated accessibility testing CI/CD pipelines

Example: Automated accessibility testing with axe-core

// Install packages
npm install --save-dev axe-core @axe-core/react jest-axe cypress-axe

// 1. axe-core in React (development mode)
// index.tsx or main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

if (process.env.NODE_ENV !== 'production') {
  import('@axe-core/react').then(axe => {
    axe.default(React, ReactDOM, 1000);
  });
}

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);

// 2. jest-axe for unit tests
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

test('should not have accessibility violations', async () => {
  const { container } = render(<MyComponent />);
  const results = await axe(container);
  
  expect(results).toHaveNoViolations();
});

// Test specific component
test('button should be accessible', async () => {
  const { container } = render(
    <button>Click me</button>
  );
  
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

// Test with custom rules
test('form should be accessible', async () => {
  const { container } = render(<LoginForm />);
  
  const results = await axe(container, {
    rules: {
      'color-contrast': { enabled: true },
      'label': { enabled: true },
    }
  });
  
  expect(results).toHaveNoViolations();
});

// 3. cypress-axe for E2E tests
// cypress/support/e2e.ts
import 'cypress-axe';

// cypress/e2e/accessibility.cy.ts
describe('Accessibility tests', () => {
  beforeEach(() => {
    cy.visit('/');
    cy.injectAxe();
  });

  it('should not have accessibility violations on home page', () => {
    cy.checkA11y();
  });

  it('should not have violations after interaction', () => {
    cy.get('button').click();
    cy.checkA11y();
  });

  it('should check specific element', () => {
    cy.checkA11y('.navigation');
  });

  it('should check with options', () => {
    cy.checkA11y(null, {
      runOnly: {
        type: 'tag',
        values: ['wcag2a', 'wcag2aa'],
      },
    });
  });

  it('should exclude specific elements', () => {
    cy.checkA11y({
      exclude: ['.advertisement'],
    });
  });
});

// 4. Playwright with axe-core
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('should not have accessibility violations', async ({ page }) => {
  await page.goto('/');
  
  const accessibilityScanResults = await new AxeBuilder({ page })
    .analyze();
  
  expect(accessibilityScanResults.violations).toEqual([]);
});

test('should check specific section', async ({ page }) => {
  await page.goto('/');
  
  const results = await new AxeBuilder({ page })
    .include('.main-content')
    .exclude('.ads')
    .analyze();
  
  expect(results.violations).toEqual([]);
});

// 5. pa11y for CI/CD
// package.json
{
  "scripts": {
    "test:a11y": "pa11y-ci"
  }
}

// .pa11yci.json
{
  "defaults": {
    "timeout": 5000,
    "standard": "WCAG2AA",
    "runners": ["axe", "htmlcs"]
  },
  "urls": [
    "http://localhost:3000",
    "http://localhost:3000/about",
    "http://localhost:3000/contact"
  ]
}

// 6. Custom axe-core integration
import { run } from 'axe-core';

async function checkAccessibility(element) {
  try {
    const results = await run(element || document.body, {
      runOnly: {
        type: 'tag',
        values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'],
      },
    });

    if (results.violations.length > 0) {
      console.error('Accessibility violations:', results.violations);
      
      results.violations.forEach(violation => {
        console.group(violation.help);
        console.log('Impact:', violation.impact);
        console.log('Description:', violation.description);
        console.log('Nodes:', violation.nodes);
        console.groupEnd();
      });
    } else {
      console.log('✓ No accessibility violations found');
    }

    return results;
  } catch (error) {
    console.error('Accessibility check failed:', error);
  }
}

// Usage in development
if (process.env.NODE_ENV === 'development') {
  window.checkA11y = checkAccessibility;
  // In console: checkA11y()
}

// 7. GitHub Actions CI
// .github/workflows/accessibility.yml
name: Accessibility Tests
on: [push, pull_request]

jobs:
  a11y:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run build
      - run: npm run test:a11y

// 8. React Testing Library with axe
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { axe } from 'jest-axe';

test('modal should be accessible', async () => {
  const { container } = render(<App />);
  
  // Open modal
  await userEvent.click(screen.getByRole('button', { name: 'Open' }));
  
  // Check accessibility
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

Accessibility Best Practices Summary

  • Semantic HTML - Use proper HTML5 elements (header, nav, main, article, aside, footer) with heading hierarchy
  • ARIA Attributes - Use aria-label, aria-labelledby, aria-describedby for context; aria-live for dynamic content; aria-expanded for state
  • Focus Management - Implement roving tabindex for toolbars/menus, focus traps for modals, restore focus after interactions
  • Color Contrast - WCAG AA requires 4.5:1 for normal text, 3:1 for large text and UI components; use :focus-visible for keyboard users
  • Keyboard Navigation - Tab/Shift+Tab for focus, Enter/Space to activate, Arrow keys for navigation, Escape to close, Home/End to jump
  • Automated Testing - Use axe-core in development (@axe-core/react), jest-axe for unit tests, cypress-axe for E2E, pa11y for CI/CD
  • Manual Testing - Test with keyboard only, screen readers (NVDA, JAWS, VoiceOver), axe DevTools browser extension

8. Error Handling Implementation Patterns

8.1 React Error Boundaries getDerivedStateFromError

Method Purpose When Called Return Value
getDerivedStateFromError Update state for fallback UI After error thrown in descendant State object to trigger re-render
componentDidCatch Log error information After error thrown, commit phase void (side effects allowed)
Error Boundary Catches React render errors Render, lifecycle, constructors Class component only
resetErrorBoundary Reset error state User action to retry Re-render children
react-error-boundary Library with hooks Simplifies error boundaries useErrorHandler hook

Example: React Error Boundaries implementation

// Basic Error Boundary class component
import React, { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
}

interface State {
  hasError: boolean;
  error: Error | null;
}

class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error: Error): State {
    // Update state to trigger fallback UI
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    // Log error to service
    console.error('Error caught by boundary:', error, errorInfo);
    
    // Send to error tracking service
    logErrorToService(error, errorInfo);
  }

  resetErrorBoundary = () => {
    this.setState({ hasError: false, error: null });
  };

  render() {
    if (this.state.hasError) {
      return (
        this.props.fallback || (
          <div role="alert">
            <h2>Something went wrong</h2>
            <details>
              <summary>Error details</summary>
              <pre>{this.state.error?.message}</pre>
            </details>
            <button onClick={this.resetErrorBoundary}>
              Try again
            </button>
          </div>
        )
      );
    }

    return this.props.children;
  }
}

// Usage
function App() {
  return (
    <ErrorBoundary fallback={<ErrorFallback />}>
      <MainContent />
    </ErrorBoundary>
  );
}

// Multiple error boundaries for granular handling
function Dashboard() {
  return (
    <div>
      <ErrorBoundary fallback={<div>Header error</div>}>
        <Header />
      </ErrorBoundary>

      <ErrorBoundary fallback={<div>Sidebar error</div>}>
        <Sidebar />
      </ErrorBoundary>

      <ErrorBoundary fallback={<div>Main content error</div>}>
        <MainContent />
      </ErrorBoundary>
    </div>
  );
}

// react-error-boundary library (recommended)
import { ErrorBoundary, useErrorHandler } from 'react-error-boundary';

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <h2>Oops! Something went wrong</h2>
      <pre style={{ color: 'red' }}>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

function App() {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onError={(error, errorInfo) => {
        console.error('Error logged:', error, errorInfo);
      }}
      onReset={() => {
        // Reset app state
        window.location.href = '/';
      }}
    >
      <MyApp />
    </ErrorBoundary>
  );
}

// useErrorHandler hook for async errors
function UserProfile({ userId }) {
  const handleError = useErrorHandler();

  useEffect(() => {
    fetchUser(userId).catch(handleError);
  }, [userId, handleError]);

  return <div>Profile</div>;
}

// Error boundary with retry logic
function AppWithRetry() {
  const [resetKey, setResetKey] = useState(0);

  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onReset={() => setResetKey(key => key + 1)}
      resetKeys={[resetKey]}
    >
      <MyApp key={resetKey} />
    </ErrorBoundary>
  );
}

// Limitations: Error boundaries DON'T catch:
// - Event handlers (use try-catch)
// - Async code (setTimeout, promises)
// - Server-side rendering
// - Errors in error boundary itself

// Handle event handler errors
function MyComponent() {
  const handleError = useErrorHandler();

  const handleClick = async () => {
    try {
      await riskyOperation();
    } catch (error) {
      handleError(error); // Pass to error boundary
    }
  };

  return <button onClick={handleClick}>Click</button>;
}

8.2 Vue errorCaptured Global Error Handler

Method Syntax Description Use Case
errorCaptured errorCaptured(err, instance, info) Component-level error handler Catch errors in descendants
app.config.errorHandler app.config.errorHandler = fn Global error handler Catch all uncaught errors
onErrorCaptured onErrorCaptured(fn) Composition API hook Handle errors in setup
Return false Stop propagation Prevent error from bubbling Local error handling
warnHandler app.config.warnHandler Handle Vue warnings Development warnings

Example: Vue error handling patterns

// Vue 3 global error handler
import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

// Global error handler
app.config.errorHandler = (err, instance, info) => {
  console.error('Global error:', err);
  console.log('Component:', instance);
  console.log('Error info:', info);

  // Send to error tracking service
  logErrorToService(err, { component: instance?.$options.name, info });
};

// Global warning handler (development)
app.config.warnHandler = (msg, instance, trace) => {
  console.warn('Vue warning:', msg);
  console.log('Trace:', trace);
};

app.mount('#app');

// Component-level error handling with errorCaptured
<script>
export default {
  name: 'ErrorBoundary',
  data() {
    return {
      error: null,
    };
  },
  errorCaptured(err, instance, info) {
    // Capture error from child components
    this.error = err;
    console.error('Error captured:', err, info);

    // Return false to stop propagation
    return false;
  },
  render() {
    if (this.error) {
      return this.$slots.fallback?.() || 'Something went wrong';
    }
    return this.$slots.default?.();
  },
};
</script>

// Usage
<template>
  <ErrorBoundary>
    <template #default>
      <ChildComponent />
    </template>
    <template #fallback>
      <div>Error occurred in child</div>
    </template>
  </ErrorBoundary>
</template>

// Vue 3 Composition API with onErrorCaptured
<script setup>
import { ref, onErrorCaptured } from 'vue';

const error = ref(null);

onErrorCaptured((err, instance, info) => {
  error.value = err;
  console.error('Error in descendant:', err, info);
  
  // Return false to stop propagation
  return false;
});
</script>

<template>
  <div>
    <div v-if="error" class="error">
      <h3>Error: {{ error.message }}</h3>
      <button @click="error = null">Reset</button>
    </div>
    <slot v-else />
  </div>
</template>

// Async error handling in Vue
<script setup>
import { ref } from 'vue';

const data = ref(null);
const error = ref(null);
const loading = ref(false);

async function fetchData() {
  loading.value = true;
  error.value = null;
  
  try {
    const response = await fetch('/api/data');
    if (!response.ok) throw new Error('Failed to fetch');
    data.value = await response.json();
  } catch (err) {
    error.value = err;
    console.error('Fetch error:', err);
  } finally {
    loading.value = false;
  }
}
</script>

<template>
  <div>
    <button @click="fetchData" :disabled="loading">
      {{ loading ? 'Loading...' : 'Fetch Data' }}
    </button>
    
    <div v-if="error" class="error">
      Error: {{ error.message }}
    </div>
    
    <div v-else-if="data">
      {{ data }}
    </div>
  </div>
</template>

// Reusable error boundary composable
import { ref, onErrorCaptured } from 'vue';

export function useErrorBoundary() {
  const error = ref(null);
  const errorInfo = ref(null);

  onErrorCaptured((err, instance, info) => {
    error.value = err;
    errorInfo.value = info;
    return false;
  });

  const reset = () => {
    error.value = null;
    errorInfo.value = null;
  };

  return { error, errorInfo, reset };
}

// Usage
<script setup>
const { error, errorInfo, reset } = useErrorBoundary();
</script>

<template>
  <div v-if="error">
    <h3>Error occurred</h3>
    <p>{{ error.message }}</p>
    <p>Info: {{ errorInfo }}</p>
    <button @click="reset">Try Again</button>
  </div>
  <slot v-else />
</template>

8.3 Sentry LogRocket Error Monitoring

Tool Purpose Features Use Case
Sentry Error tracking & monitoring Stack traces, releases, breadcrumbs Production error tracking
LogRocket Session replay & monitoring Video replay, console logs, network Debug user sessions
Source Maps Map minified to original code Readable stack traces Production debugging
Breadcrumbs Event trail before error User actions, network, console Error context
Performance Monitor app performance Transactions, spans, metrics Performance tracking
User Context Identify affected users User ID, email, metadata User-specific issues

Example: Sentry and LogRocket integration

// Install packages
npm install @sentry/react logrocket logrocket-react

// Sentry initialization (React)
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';

Sentry.init({
  dsn: 'YOUR_SENTRY_DSN',
  integrations: [
    new BrowserTracing(),
    new Sentry.Replay({
      maskAllText: false,
      blockAllMedia: false,
    }),
  ],
  
  // Performance monitoring
  tracesSampleRate: 1.0, // 100% in dev, lower in prod
  
  // Session replay
  replaysSessionSampleRate: 0.1, // 10% of sessions
  replaysOnErrorSampleRate: 1.0, // 100% when error occurs
  
  // Environment
  environment: process.env.NODE_ENV,
  release: process.env.REACT_APP_VERSION,
  
  // Filter errors
  beforeSend(event, hint) {
    // Don't send errors in development
    if (process.env.NODE_ENV === 'development') {
      return null;
    }
    
    // Filter out specific errors
    if (event.exception?.values?.[0]?.value?.includes('ResizeObserver')) {
      return null;
    }
    
    return event;
  },
});

// Wrap app with Sentry
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'));
root.render(
  <Sentry.ErrorBoundary fallback={ErrorFallback} showDialog>
    <App />
  </Sentry.ErrorBoundary>
);

// Set user context
Sentry.setUser({
  id: user.id,
  email: user.email,
  username: user.name,
});

// Add breadcrumbs manually
Sentry.addBreadcrumb({
  category: 'auth',
  message: 'User logged in',
  level: 'info',
});

// Capture exceptions manually
try {
  riskyOperation();
} catch (error) {
  Sentry.captureException(error);
}

// Capture messages
Sentry.captureMessage('Something went wrong', 'warning');

// Performance monitoring
const transaction = Sentry.startTransaction({ name: 'checkout' });

// Child spans
const span = transaction.startChild({
  op: 'payment',
  description: 'Process payment',
});

await processPayment();
span.finish();

transaction.finish();

// React component with Sentry profiling
import { withProfiler } from '@sentry/react';

function MyComponent() {
  return <div>Content</div>;
}

export default withProfiler(MyComponent);

// LogRocket initialization
import LogRocket from 'logrocket';
import setupLogRocketReact from 'logrocket-react';

LogRocket.init('YOUR_LOGROCKET_APP_ID', {
  release: process.env.REACT_APP_VERSION,
  console: {
    shouldAggregateConsoleErrors: true,
  },
  network: {
    requestSanitizer: (request) => {
      // Remove sensitive data
      if (request.headers['Authorization']) {
        request.headers['Authorization'] = '[REDACTED]';
      }
      return request;
    },
  },
});

// Setup LogRocket React plugin
setupLogRocketReact(LogRocket);

// Identify user
LogRocket.identify(user.id, {
  name: user.name,
  email: user.email,
  subscriptionType: user.plan,
});

// Track custom events
LogRocket.track('Checkout Completed', {
  orderId: order.id,
  total: order.total,
});

// Integrate Sentry + LogRocket
import * as Sentry from '@sentry/react';
import LogRocket from 'logrocket';

LogRocket.getSessionURL((sessionURL) => {
  Sentry.configureScope((scope) => {
    scope.setExtra('sessionURL', sessionURL);
  });
});

// Also add to error context
Sentry.init({
  beforeSend(event) {
    const sessionURL = LogRocket.sessionURL;
    if (sessionURL) {
      event.extra = event.extra || {};
      event.extra.sessionURL = sessionURL;
    }
    return event;
  },
});

// Error boundary with Sentry + LogRocket
function ErrorFallback({ error, resetErrorBoundary }) {
  useEffect(() => {
    // Send to Sentry
    Sentry.captureException(error);
    
    // LogRocket will automatically capture
    LogRocket.captureException(error);
  }, [error]);

  return (
    <div>
      <h2>Something went wrong</h2>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
      <button onClick={() => Sentry.showReportDialog()}>
        Report feedback
      </button>
    </div>
  );
}

// Upload source maps to Sentry
// package.json
{
  "scripts": {
    "build": "react-scripts build && sentry-cli releases files $npm_package_version upload-sourcemaps ./build"
  }
}

// .sentryclirc
[defaults]
org = your-org
project = your-project

[auth]
token = YOUR_AUTH_TOKEN

// Next.js with Sentry
// next.config.js
const { withSentryConfig } = require('@sentry/nextjs');

module.exports = withSentryConfig(
  {
    // Next.js config
  },
  {
    // Sentry webpack plugin options
    silent: true,
    org: 'your-org',
    project: 'your-project',
  }
);

8.4 Toast Notifications react-hot-toast

Method Syntax Description Use Case
toast.success toast.success('Saved!') Success notification Successful operations
toast.error toast.error('Failed!') Error notification Failed operations
toast.loading toast.loading('Saving...') Loading notification Async operations
toast.promise toast.promise(promise, msgs) Auto-updates based on promise Async with feedback
toast.custom toast.custom(jsx) Custom notification component Custom designs
Position position: 'top-right' Toast position on screen UX preference

Example: Toast notifications for user feedback

// Install react-hot-toast
npm install react-hot-toast

// Basic setup
import { Toaster, toast } from 'react-hot-toast';

function App() {
  return (
    <div>
      <Toaster
        position="top-right"
        reverseOrder={false}
        gutter={8}
        toastOptions={{
          duration: 4000,
          style: {
            background: '#363636',
            color: '#fff',
          },
          success: {
            duration: 3000,
            iconTheme: {
              primary: '#4ade80',
              secondary: '#fff',
            },
          },
          error: {
            duration: 5000,
            iconTheme: {
              primary: '#ef4444',
              secondary: '#fff',
            },
          },
        }}
      />
      <MyApp />
    </div>
  );
}

// Basic toast notifications
function FormExample() {
  const handleSubmit = async (data) => {
    try {
      await saveData(data);
      toast.success('Successfully saved!');
    } catch (error) {
      toast.error('Failed to save. Please try again.');
    }
  };

  return <form onSubmit={handleSubmit}>...</form>;
}

// Loading toast with manual control
function AsyncOperation() {
  const handleAction = async () => {
    const toastId = toast.loading('Processing...');
    
    try {
      await performAsyncOperation();
      toast.success('Completed!', { id: toastId });
    } catch (error) {
      toast.error('Failed!', { id: toastId });
    }
  };

  return <button onClick={handleAction}>Start</button>;
}

// Promise-based toast (automatic)
function PromiseExample() {
  const handleSave = () => {
    const savePromise = fetch('/api/save', {
      method: 'POST',
      body: JSON.stringify(data),
    }).then(res => res.json());

    toast.promise(savePromise, {
      loading: 'Saving...',
      success: 'Saved successfully!',
      error: 'Could not save.',
    });
  };

  return <button onClick={handleSave}>Save</button>;
}

// Custom toast with JSX
function CustomToast() {
  const notify = () => {
    toast.custom((t) => (
      <div
        className={`${
          t.visible ? 'animate-enter' : 'animate-leave'
        } bg-white shadow-lg rounded-lg p-4`}
      >
        <div className="flex items-center">
          <div className="flex-shrink-0">
            <CheckIcon />
          </div>
          <div className="ml-3">
            <h3 className="font-medium">Custom notification</h3>
            <p className="text-sm text-gray-500">This is a custom toast</p>
          </div>
          <button onClick={() => toast.dismiss(t.id)}>
            <XIcon />
          </button>
        </div>
      </div>
    ));
  };

  return <button onClick={notify}>Show Custom Toast</button>;
}

// Toast with action button
function ToastWithAction() {
  const handleDelete = () => {
    toast((t) => (
      <div>
        <p>Are you sure you want to delete?</p>
        <div className="flex gap-2 mt-2">
          <button
            onClick={() => {
              performDelete();
              toast.dismiss(t.id);
              toast.success('Deleted');
            }}
          >
            Delete
          </button>
          <button onClick={() => toast.dismiss(t.id)}>
            Cancel
          </button>
        </div>
      </div>
    ), { duration: Infinity });
  };

  return <button onClick={handleDelete}>Delete Item</button>;
}

// Different toast types
function ToastExamples() {
  return (
    <div>
      <button onClick={() => toast('Basic message')}>
        Default
      </button>
      
      <button onClick={() => toast.success('Success!')}>
        Success
      </button>
      
      <button onClick={() => toast.error('Error!')}>
        Error
      </button>
      
      <button onClick={() => toast.loading('Loading...')}>
        Loading
      </button>
      
      <button onClick={() => toast('Info', { icon: 'ℹ️' })}>
        Info
      </button>
    </div>
  );
}

// Custom styling
toast.success('Styled toast', {
  style: {
    border: '1px solid #713200',
    padding: '16px',
    color: '#713200',
  },
  iconTheme: {
    primary: '#713200',
    secondary: '#FFFAEE',
  },
});

// Dismiss all toasts
toast.dismiss();

// Dismiss specific toast
const toastId = toast.loading('Processing...');
toast.dismiss(toastId);

// Alternative: react-toastify
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

function App() {
  return (
    <>
      <ToastContainer
        position="top-right"
        autoClose={5000}
        hideProgressBar={false}
        newestOnTop
        closeOnClick
        rtl={false}
        pauseOnFocusLoss
        draggable
        pauseOnHover
      />
      <MyApp />
    </>
  );
}

toast.success('Success!');
toast.error('Error!');
toast.info('Info');
toast.warn('Warning');

8.5 Retry Logic Exponential Backoff

Strategy Formula Description Use Case
Exponential Backoff delay = baseDelay * 2^attempt Doubles delay between retries API rate limiting
Linear Backoff delay = baseDelay * attempt Linear increase in delay Simple retry scenarios
Jitter delay += random(0, jitter) Add randomness to prevent thundering herd Multiple clients
Max Retries Limit retry attempts Prevent infinite loops All retry scenarios
Circuit Breaker Stop retries after threshold Fail fast when service down Microservices

Example: Retry logic with exponential backoff

// Basic exponential backoff
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  let lastError;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      
      return await response.json();
    } catch (error) {
      lastError = error;
      
      // Don't retry on last attempt
      if (attempt === maxRetries) {
        break;
      }

      // Calculate exponential backoff delay
      const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
      console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
      
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  throw lastError;
}

// Usage
try {
  const data = await fetchWithRetry('/api/data');
  console.log(data);
} catch (error) {
  console.error('Failed after retries:', error);
}

// Advanced retry with jitter
function calculateBackoff(attempt, baseDelay = 1000, maxDelay = 30000) {
  // Exponential backoff
  const exponentialDelay = baseDelay * Math.pow(2, attempt);
  
  // Add jitter (randomness)
  const jitter = Math.random() * 0.3 * exponentialDelay;
  
  // Cap at max delay
  return Math.min(exponentialDelay + jitter, maxDelay);
}

async function retryWithBackoff(fn, options = {}) {
  const {
    maxRetries = 3,
    baseDelay = 1000,
    maxDelay = 30000,
    shouldRetry = () => true,
    onRetry = () => {},
  } = options;

  let lastError;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;

      // Check if we should retry
      if (attempt === maxRetries || !shouldRetry(error, attempt)) {
        break;
      }

      // Calculate backoff delay
      const delay = calculateBackoff(attempt, baseDelay, maxDelay);
      
      // Notify retry
      onRetry(error, attempt, delay);

      // Wait before retry
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  throw lastError;
}

// Usage with custom retry conditions
const data = await retryWithBackoff(
  () => fetch('/api/data').then(r => r.json()),
  {
    maxRetries: 5,
    baseDelay: 500,
    maxDelay: 10000,
    shouldRetry: (error, attempt) => {
      // Retry on network errors or 5xx
      return error.message.includes('Failed to fetch') ||
             (error.status >= 500 && error.status < 600);
    },
    onRetry: (error, attempt, delay) => {
      console.log(`Retrying (${attempt + 1}) after ${delay}ms: ${error.message}`);
      toast.error(`Request failed, retrying... (${attempt + 1})`);
    },
  }
);

// React hook for retry logic
function useRetry(fn, options = {}) {
  const [state, setState] = useState({
    data: null,
    error: null,
    loading: false,
    retryCount: 0,
  });

  const execute = useCallback(async (...args) => {
    setState(prev => ({ ...prev, loading: true, error: null }));

    try {
      const result = await retryWithBackoff(
        () => fn(...args),
        {
          ...options,
          onRetry: (error, attempt) => {
            setState(prev => ({ ...prev, retryCount: attempt + 1 }));
            options.onRetry?.(error, attempt);
          },
        }
      );

      setState({
        data: result,
        error: null,
        loading: false,
        retryCount: 0,
      });

      return result;
    } catch (error) {
      setState(prev => ({
        ...prev,
        error,
        loading: false,
      }));
      throw error;
    }
  }, [fn, options]);

  return { ...state, execute };
}

// Usage in component
function DataComponent() {
  const { data, error, loading, retryCount, execute } = useRetry(
    () => fetch('/api/data').then(r => r.json()),
    { maxRetries: 3 }
  );

  useEffect(() => {
    execute();
  }, []);

  if (loading) return <div>Loading... {retryCount > 0 && `(Retry ${retryCount})`}</div>;
  if (error) return <div>Error: {error.message} <button onClick={execute}>Retry</button></div>;
  return <div>{JSON.stringify(data)}</div>;
}

// Circuit breaker pattern
class CircuitBreaker {
  constructor(threshold = 5, timeout = 60000) {
    this.failureThreshold = threshold;
    this.resetTimeout = timeout;
    this.failureCount = 0;
    this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
    this.nextAttempt = Date.now();
  }

  async execute(fn) {
    if (this.state === 'OPEN') {
      if (Date.now() < this.nextAttempt) {
        throw new Error('Circuit breaker is OPEN');
      }
      this.state = 'HALF_OPEN';
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  onSuccess() {
    this.failureCount = 0;
    this.state = 'CLOSED';
  }

  onFailure() {
    this.failureCount++;
    if (this.failureCount >= this.failureThreshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.resetTimeout;
      console.log('Circuit breaker opened');
    }
  }
}

// Usage
const breaker = new CircuitBreaker(3, 30000);

async function callAPI() {
  return breaker.execute(() => fetch('/api/data').then(r => r.json()));
}

8.6 Fallback UI Skeleton Loading States

Pattern Implementation Description Use Case
Skeleton Screens Placeholder with shape Shows content structure while loading Better perceived performance
Shimmer Effect Animated gradient Indicates loading in progress Visual feedback
Suspense <Suspense fallback={...}> React's declarative loading Code splitting, data fetching
Empty States No data placeholder Shows when no content available Empty lists, no results
Error States Error message with retry Shows when loading fails Failed data fetching
Progressive Loading Load in stages Load critical content first Large pages

Example: Skeleton screens and loading states

// Basic skeleton component
function Skeleton({ width = '100%', height = '20px', className = '' }) {
  return (
    <div
      className={`skeleton ${className}`}
      style={{
        width,
        height,
        backgroundColor: '#e0e0e0',
        borderRadius: '4px',
        animation: 'pulse 1.5s ease-in-out infinite',
      }}
    />
  );
}

// CSS for shimmer effect
.skeleton {
  background: linear-gradient(
    90deg,
    #e0e0e0 25%,
    #f0f0f0 50%,
    #e0e0e0 75%
  );
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}

@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

// Card skeleton
function CardSkeleton() {
  return (
    <div className="card">
      <Skeleton height="200px" className="mb-4" />
      <Skeleton height="24px" width="80%" className="mb-2" />
      <Skeleton height="16px" width="100%" className="mb-1" />
      <Skeleton height="16px" width="90%" />
    </div>
  );
}

// List skeleton
function ListSkeleton({ count = 5 }) {
  return (
    <div>
      {Array.from({ length: count }).map((_, i) => (
        <div key={i} className="flex items-center gap-4 p-4">
          <Skeleton width="50px" height="50px" style={{ borderRadius: '50%' }} />
          <div className="flex-1">
            <Skeleton height="16px" width="40%" className="mb-2" />
            <Skeleton height="14px" width="80%" />
          </div>
        </div>
      ))}
    </div>
  );
}

// Data fetching with loading states
function UserProfile({ userId }) {
  const [state, setState] = useState({
    data: null,
    loading: true,
    error: null,
  });

  useEffect(() => {
    fetchUser(userId)
      .then(data => setState({ data, loading: false, error: null }))
      .catch(error => setState({ data: null, loading: false, error }));
  }, [userId]);

  if (state.loading) return <CardSkeleton />;
  
  if (state.error) {
    return (
      <div className="error-state">
        <h3>Failed to load user</h3>
        <p>{state.error.message}</p>
        <button onClick={() => window.location.reload()}>
          Try Again
        </button>
      </div>
    );
  }

  if (!state.data) {
    return (
      <div className="empty-state">
        <p>No user found</p>
      </div>
    );
  }

  return (
    <div className="user-profile">
      <img src={state.data.avatar} alt={state.data.name} />
      <h2>{state.data.name}</h2>
      <p>{state.data.bio}</p>
    </div>
  );
}

// React Suspense with skeleton
import { Suspense, lazy } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<CardSkeleton />}>
      <HeavyComponent />
    </Suspense>
  );
}

// Custom loading hook
function useLoadingState(initialLoading = false) {
  const [loading, setLoading] = useState(initialLoading);
  const [error, setError] = useState(null);

  const withLoading = useCallback(async (fn) => {
    setLoading(true);
    setError(null);
    
    try {
      const result = await fn();
      return result;
    } catch (err) {
      setError(err);
      throw err;
    } finally {
      setLoading(false);
    }
  }, []);

  return { loading, error, withLoading };
}

// Usage
function DataComponent() {
  const [data, setData] = useState(null);
  const { loading, error, withLoading } = useLoadingState(true);

  useEffect(() => {
    withLoading(async () => {
      const result = await fetchData();
      setData(result);
    });
  }, []);

  if (loading) return <Skeleton />;
  if (error) return <ErrorState error={error} />;
  return <div>{data}</div>;
}

// Skeleton library: react-loading-skeleton
import Skeleton from 'react-loading-skeleton';
import 'react-loading-skeleton/dist/skeleton.css';

function ProfileSkeleton() {
  return (
    <div>
      <Skeleton circle width={100} height={100} />
      <Skeleton count={3} />
    </div>
  );
}

// Progressive loading
function Dashboard() {
  return (
    <div>
      {/* Critical content loads first */}
      <Header />
      
      {/* Non-critical wrapped in Suspense */}
      <Suspense fallback={<ChartSkeleton />}>
        <AnalyticsChart />
      </Suspense>

      <Suspense fallback={<TableSkeleton />}>
        <DataTable />
      </Suspense>
    </div>
  );
}

// Empty state component
function EmptyState({ title, description, action }) {
  return (
    <div className="empty-state">
      <EmptyIcon />
      <h3>{title}</h3>
      <p>{description}</p>
      {action && <button onClick={action.onClick}>{action.label}</button>}
    </div>
  );
}

// Usage
function ProductList({ products }) {
  if (products.length === 0) {
    return (
      <EmptyState
        title="No products found"
        description="Try adjusting your filters"
        action={{ label: 'Clear filters', onClick: clearFilters }}
      />
    );
  }

  return products.map(product => <ProductCard key={product.id} {...product} />);
}

Error Handling Best Practices

  • Error Boundaries - Use React Error Boundaries (getDerivedStateFromError, componentDidCatch) or react-error-boundary library for catching render errors
  • Vue Error Handling - Use errorCaptured lifecycle hook in components, app.config.errorHandler for global errors, onErrorCaptured in Composition API
  • Error Monitoring - Integrate Sentry for error tracking with source maps, LogRocket for session replay, add breadcrumbs and user context
  • User Feedback - Use toast notifications (react-hot-toast) for success/error/loading states, show clear error messages with retry options
  • Retry Logic - Implement exponential backoff with jitter, limit max retries, use circuit breaker pattern for failing services
  • Loading States - Show skeleton screens during loading, empty states for no data, error states with retry, use React Suspense for declarative loading
  • Production Ready - Filter sensitive errors before sending to monitoring, upload source maps for readable stack traces, set up alerts for critical errors

9. Performance Optimization Implementation

9.1 Core Web Vitals LCP CLS FID

Metric Measures Good Target Optimization
LCP (Largest Contentful Paint) Loading performance < 2.5s Optimize images, server response, render-blocking resources
CLS (Cumulative Layout Shift) Visual stability < 0.1 Set image/video dimensions, avoid inserting content, use CSS transforms
FID (First Input Delay) Interactivity < 100ms Reduce JS execution time, code splitting, web workers
INP (Interaction to Next Paint) Responsiveness (replaces FID) < 200ms Optimize event handlers, reduce main thread work
TTFB (Time to First Byte) Server response < 800ms CDN, server optimization, caching
FCP (First Contentful Paint) First render < 1.8s Inline critical CSS, preload fonts, optimize server

Example: Core Web Vitals measurement and optimization

// Measure Core Web Vitals with web-vitals library
npm install web-vitals

import { onCLS, onFID, onLCP, onINP, onFCP, onTTFB } from 'web-vitals';

// Send to analytics
function sendToAnalytics(metric) {
  const body = JSON.stringify(metric);
  
  // Use sendBeacon if available (doesn't block page unload)
  if (navigator.sendBeacon) {
    navigator.sendBeacon('/analytics', body);
  } else {
    fetch('/analytics', { body, method: 'POST', keepalive: true });
  }
}

// Measure all Core Web Vitals
onCLS(sendToAnalytics);
onFID(sendToAnalytics);
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);

// React hook for Core Web Vitals
import { useEffect } from 'react';

function useWebVitals(onMetric) {
  useEffect(() => {
    import('web-vitals').then(({ onCLS, onFID, onLCP }) => {
      onCLS(onMetric);
      onFID(onMetric);
      onLCP(onMetric);
    });
  }, [onMetric]);
}

// Usage
function App() {
  useWebVitals((metric) => {
    console.log(metric.name, metric.value);
  });

  return <div>App</div>;
}

// Optimize LCP - Preload critical resources
<head>
  {/* Preload hero image */}
  <link rel="preload" as="image" href="/hero.jpg" />
  
  {/* Preload critical fonts */}
  <link
    rel="preload"
    as="font"
    href="/fonts/inter.woff2"
    type="font/woff2"
    crossOrigin="anonymous"
  />
  
  {/* Preconnect to external domains */}
  <link rel="preconnect" href="https://api.example.com" />
  <link rel="dns-prefetch" href="https://cdn.example.com" />
</head>

// Optimize CLS - Reserve space for images
<img
  src="image.jpg"
  alt="Description"
  width="800"
  height="600"
  style={{ aspectRatio: '800/600' }}
/>

// Use CSS aspect-ratio
.image-container {
  aspect-ratio: 16 / 9;
}

// Avoid layout shifts from dynamic content
.ad-container {
  min-height: 250px; /* Reserve space */
}

// Use CSS transform instead of top/left
// Bad (causes layout shift)
.element {
  top: 100px;
  transition: top 0.3s;
}

// Good (no layout shift)
.element {
  transform: translateY(100px);
  transition: transform 0.3s;
}

// Optimize FID/INP - Code splitting
import { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <HeavyComponent />
    </Suspense>
  );
}

// Use Web Workers for heavy computation
// worker.js
self.addEventListener('message', (e) => {
  const result = heavyComputation(e.data);
  self.postMessage(result);
});

// main.js
const worker = new Worker('worker.js');
worker.postMessage(data);
worker.addEventListener('message', (e) => {
  console.log('Result:', e.data);
});

// Optimize TTFB - Next.js with CDN
// next.config.js
module.exports = {
  images: {
    domains: ['cdn.example.com'],
  },
  headers: async () => [
    {
      source: '/:path*',
      headers: [
        {
          key: 'Cache-Control',
          value: 'public, max-age=31536000, immutable',
        },
      ],
    },
  ],
};

// Lighthouse CI for continuous monitoring
// .lighthouserc.js
module.exports = {
  ci: {
    collect: {
      url: ['http://localhost:3000'],
      numberOfRuns: 3,
    },
    assert: {
      preset: 'lighthouse:recommended',
      assertions: {
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
        'first-input-delay': ['error', { maxNumericValue: 100 }],
      },
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
};

// Performance Observer API
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(entry.name, entry.startTime, entry.duration);
  }
});

// Observe different entry types
observer.observe({ entryTypes: ['measure', 'navigation', 'resource'] });

// Custom performance marks
performance.mark('start-api-call');
await fetchData();
performance.mark('end-api-call');

performance.measure('api-call-duration', 'start-api-call', 'end-api-call');

// Next.js built-in Web Vitals reporting
// pages/_app.js
export function reportWebVitals(metric) {
  console.log(metric);
  
  // Send to analytics service
  if (metric.label === 'web-vital') {
    gtag('event', metric.name, {
      value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
      event_label: metric.id,
      non_interaction: true,
    });
  }
}

9.2 Code Splitting React.lazy Suspense

Technique Implementation Description Use Case
React.lazy lazy(() => import('./Component')) Dynamic import for components Route-based splitting
Suspense <Suspense fallback={...}> Loading boundary for lazy components Show loading state
Route-based Split by routes Load route code on navigation Multi-page apps
Component-based Split heavy components Load on demand (modal, chart) Conditional features
Webpack magic comments /* webpackChunkName: "name" */ Name chunks, prefetch/preload Better caching
Dynamic import import('module').then() Load modules conditionally Polyfills, libraries

Example: Code splitting strategies

// Basic React.lazy with Suspense
import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </Suspense>
  );
}

// Route-based code splitting with React Router
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { lazy, Suspense } from 'react';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));
const Dashboard = lazy(() => import('./pages/Dashboard'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<PageLoader />}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/contact" element={<Contact />} />
          <Route path="/dashboard/*" element={<Dashboard />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

// Component-based splitting (modal, heavy chart)
function ProductPage() {
  const [showModal, setShowModal] = useState(false);
  
  // Only load modal when needed
  const Modal = lazy(() => import('./Modal'));

  return (
    <div>
      <button onClick={() => setShowModal(true)}>Open</button>
      
      {showModal && (
        <Suspense fallback={<ModalSkeleton />}>
          <Modal onClose={() => setShowModal(false)} />
        </Suspense>
      )}
    </div>
  );
}

// Webpack magic comments for chunk naming
const Dashboard = lazy(() =>
  import(/* webpackChunkName: "dashboard" */ './Dashboard')
);

// Prefetch on hover (load before needed)
const Dashboard = lazy(() =>
  import(/* webpackPrefetch: true */ './Dashboard')
);

// Preload (load in parallel with parent)
const Dashboard = lazy(() =>
  import(/* webpackPreload: true */ './Dashboard')
);

// Conditional loading based on feature flags
async function loadFeature() {
  if (featureFlags.newDashboard) {
    const { NewDashboard } = await import('./NewDashboard');
    return NewDashboard;
  } else {
    const { OldDashboard } = await import('./OldDashboard');
    return OldDashboard;
  }
}

// Library splitting (load polyfills conditionally)
async function loadPolyfills() {
  if (!window.IntersectionObserver) {
    await import('intersection-observer');
  }
  
  if (!window.fetch) {
    await import('whatwg-fetch');
  }
}

// Multiple suspense boundaries
function App() {
  return (
    <div>
      <Header />
      
      {/* Critical content */}
      <Suspense fallback={<HeroSkeleton />}>
        <Hero />
      </Suspense>
      
      {/* Non-critical content */}
      <Suspense fallback={<ChartSkeleton />}>
        <AnalyticsChart />
      </Suspense>
      
      <Suspense fallback={<TableSkeleton />}>
        <DataTable />
      </Suspense>
    </div>
  );
}

// Error boundary with lazy loading
import { ErrorBoundary } from 'react-error-boundary';

function LazyRoute({ path, component }) {
  const Component = lazy(component);
  
  return (
    <ErrorBoundary fallback={<ErrorPage />}>
      <Suspense fallback={<Loading />}>
        <Component />
      </Suspense>
    </ErrorBoundary>
  );
}

// Next.js dynamic imports
import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() => import('./Component'), {
  loading: () => <p>Loading...</p>,
  ssr: false, // Disable server-side rendering
});

// With named export
const DynamicChart = dynamic(() =>
  import('./Charts').then(mod => mod.LineChart)
);

// Vite code splitting
// Vite automatically splits by dynamic imports
const AdminPanel = () => import('./AdminPanel.vue');

// Manual chunk configuration (vite.config.js)
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          router: ['react-router-dom'],
          ui: ['@mui/material', '@emotion/react'],
        },
      },
    },
  },
};

9.3 React.memo useMemo useCallback

Hook/HOC Purpose When to Use Caveat
React.memo Memoize component Expensive renders, pure components Shallow prop comparison
useMemo Memoize computed value Expensive calculations Don't overuse, memory cost
useCallback Memoize function Pass to memoized children, deps Useful with React.memo
useMemo vs useCallback Value vs function useMemo(() => fn) = useCallback(fn) useCallback is shorthand
Custom compare React.memo(Comp, areEqual) Deep comparison, specific props Complex prop structures

Example: Memoization techniques in React

// React.memo - prevent unnecessary re-renders
import { memo } from 'react';

const ExpensiveComponent = memo(({ data, onClick }) => {
  console.log('Rendering ExpensiveComponent');
  
  return (
    <div>
      {data.map(item => (
        <div key={item.id} onClick={() => onClick(item)}>
          {item.name}
        </div>
      ))}
    </div>
  );
});

// Parent component
function Parent() {
  const [count, setCount] = useState(0);
  const [data] = useState([/* large array */]);

  // Without memo, ExpensiveComponent re-renders when count changes
  // With memo, it only re-renders when data or onClick changes
  
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <ExpensiveComponent data={data} onClick={handleClick} />
    </div>
  );
}

// useCallback - memoize function reference
function Parent() {
  const [count, setCount] = useState(0);
  const [items, setItems] = useState([]);

  // Without useCallback, new function on every render
  // const handleClick = (item) => {
  //   console.log(item);
  // };

  // With useCallback, same function reference
  const handleClick = useCallback((item) => {
    console.log(item);
    // Use items from closure
  }, []); // Dependencies

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <ExpensiveComponent items={items} onClick={handleClick} />
    </div>
  );
}

// useMemo - memoize expensive computation
function DataProcessor({ rawData }) {
  // Without useMemo, processes on every render
  // const processedData = expensiveProcessing(rawData);

  // With useMemo, only recomputes when rawData changes
  const processedData = useMemo(() => {
    console.log('Processing data...');
    return expensiveProcessing(rawData);
  }, [rawData]);

  return <div>{processedData.length} items</div>;
}

// Expensive computation example
const filteredAndSortedList = useMemo(() => {
  return items
    .filter(item => item.active)
    .sort((a, b) => a.name.localeCompare(b.name));
}, [items]);

// Custom comparison function for React.memo
const ComplexComponent = memo(
  ({ user, settings }) => {
    return <div>{user.name}</div>;
  },
  (prevProps, nextProps) => {
    // Return true if props are equal (skip re-render)
    return (
      prevProps.user.id === nextProps.user.id &&
      prevProps.settings.theme === nextProps.settings.theme
    );
  }
);

// Real-world example: Search with debounce
function SearchComponent({ onSearch }) {
  const [query, setQuery] = useState('');

  // Memoize debounced function
  const debouncedSearch = useMemo(
    () => debounce((value) => onSearch(value), 300),
    [onSearch]
  );

  const handleChange = (e) => {
    const value = e.target.value;
    setQuery(value);
    debouncedSearch(value);
  };

  return <input value={query} onChange={handleChange} />;
}

// Context with memoization
const UserContext = createContext();

function UserProvider({ children }) {
  const [user, setUser] = useState(null);

  // Memoize context value to prevent unnecessary re-renders
  const value = useMemo(
    () => ({
      user,
      setUser,
      isAuthenticated: !!user,
    }),
    [user]
  );

  return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
}

// When NOT to use memoization
// ❌ Don't memoize everything
function SimpleComponent({ name }) {
  // This is fine without useMemo
  const greeting = `Hello, ${name}!`;
  
  return <div>{greeting}</div>;
}

// ❌ Primitive props don't benefit from React.memo
const SimpleChild = memo(({ text, count }) => {
  // React already optimizes primitive comparisons
  return <div>{text} {count}</div>;
});

// ✅ DO use for expensive renders or large prop objects
const DataGrid = memo(({ data, columns, onSort }) => {
  // Expensive table rendering with thousands of rows
  return <table>...</table>;
});

// Combined example: Optimized list component
const ListItem = memo(({ item, onDelete }) => {
  return (
    <li>
      {item.name}
      <button onClick={() => onDelete(item.id)}>Delete</button>
    </li>
  );
});

function List({ items }) {
  const [selectedId, setSelectedId] = useState(null);

  // Memoize callback to prevent ListItem re-renders
  const handleDelete = useCallback((id) => {
    // Delete logic
  }, []);

  // Memoize filtered items
  const filteredItems = useMemo(
    () => items.filter(item => item.visible),
    [items]
  );

  return (
    <ul>
      {filteredItems.map(item => (
        <ListItem key={item.id} item={item} onDelete={handleDelete} />
      ))}
    </ul>
  );
}

9.4 Webpack Bundle Analyzer Optimization

Tool Purpose Installation Insight
webpack-bundle-analyzer Visualize bundle size npm i -D webpack-bundle-analyzer Interactive treemap
source-map-explorer Analyze with source maps npm i -D source-map-explorer See original file sizes
Bundle Buddy Find duplicate code Web tool Identify optimization opportunities
Tree Shaking Remove dead code Built into Webpack 4+ Requires ES6 modules
Code Splitting Split into chunks Webpack config Parallel loading

Example: Bundle analysis and optimization

// Install webpack-bundle-analyzer
npm install --save-dev webpack-bundle-analyzer

// Webpack configuration
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      reportFilename: 'bundle-report.html',
      openAnalyzer: false,
      generateStatsFile: true,
      statsFilename: 'bundle-stats.json',
    }),
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10,
        },
        common: {
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true,
        },
      },
    },
  },
};

// package.json scripts
{
  "scripts": {
    "build": "webpack --mode production",
    "analyze": "webpack --mode production --profile --json > stats.json && webpack-bundle-analyzer stats.json"
  }
}

// Create React App with analyzer
npm install --save-dev cra-bundle-analyzer

// package.json
{
  "scripts": {
    "analyze": "npm run build && npx cra-bundle-analyzer"
  }
}

// Next.js with bundle analyzer
npm install @next/bundle-analyzer

// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

module.exports = withBundleAnalyzer({
  // Next.js config
});

// package.json
{
  "scripts": {
    "analyze": "ANALYZE=true npm run build"
  }
}

// Vite with rollup-plugin-visualizer
npm install --save-dev rollup-plugin-visualizer

// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer';

export default {
  plugins: [
    visualizer({
      open: true,
      gzipSize: true,
      brotliSize: true,
    }),
  ],
};

// Source map explorer
npm install --save-dev source-map-explorer

// package.json
{
  "scripts": {
    "analyze": "source-map-explorer 'build/static/js/*.js'"
  }
}

// Optimization strategies based on bundle analysis

// 1. Replace heavy libraries
// ❌ moment.js (288KB)
import moment from 'moment';
const date = moment().format('YYYY-MM-DD');

// ✅ date-fns (13KB with tree shaking)
import { format } from 'date-fns';
const date = format(new Date(), 'yyyy-MM-dd');

// 2. Import only what you need
// ❌ Import entire library
import _ from 'lodash';
const result = _.debounce(fn, 300);

// ✅ Import specific function
import debounce from 'lodash/debounce';
const result = debounce(fn, 300);

// 3. Use dynamic imports for large libraries
// ❌ Import upfront
import { Chart } from 'chart.js';

// ✅ Load on demand
const loadChart = async () => {
  const { Chart } = await import('chart.js');
  return Chart;
};

// 4. Configure externals (CDN)
// webpack.config.js
module.exports = {
  externals: {
    react: 'React',
    'react-dom': 'ReactDOM',
  },
};

// HTML
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>

// 5. Optimize images
// Use next/image or similar
import Image from 'next/image';

<Image
  src="/hero.jpg"
  width={800}
  height={600}
  alt="Hero"
  placeholder="blur"
/>

// 6. Remove unused CSS
// Use PurgeCSS or Tailwind's built-in purge
// tailwind.config.js
module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  // CSS not used in these files will be removed
};

// 7. Minify and compress
// webpack.config.js
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');

module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true, // Remove console.log
          },
        },
      }),
    ],
  },
  plugins: [
    new CompressionPlugin({
      algorithm: 'brotliCompress',
      test: /\.(js|css|html|svg)$/,
    }),
  ],
};

// 8. Analyze and set performance budgets
// webpack.config.js
module.exports = {
  performance: {
    maxAssetSize: 244 * 1024, // 244 KB
    maxEntrypointSize: 244 * 1024,
    hints: 'error', // or 'warning'
  },
};

// Custom bundle size check
const fs = require('fs');
const path = require('path');

const buildFolder = path.join(__dirname, 'build/static/js');
const files = fs.readdirSync(buildFolder);

let totalSize = 0;
files.forEach(file => {
  if (file.endsWith('.js')) {
    const stats = fs.statSync(path.join(buildFolder, file));
    totalSize += stats.size;
  }
});

console.log(`Total JS bundle size: ${(totalSize / 1024).toFixed(2)} KB`);

if (totalSize > 500 * 1024) {
  console.error('Bundle size exceeds 500KB!');
  process.exit(1);
}

9.5 Image Optimization next/image WebP

Technique Implementation Benefit Browser Support
WebP Format Modern image format 25-35% smaller than JPEG 96%+ (with fallback)
AVIF Format Newest format 50% smaller than JPEG 90%+ (newer browsers)
Lazy Loading loading="lazy" Load images as needed Native browser support
Responsive Images srcset, sizes Load appropriate size Universal support
next/image Next.js Image component Automatic optimization Works everywhere
CDN Image transformation API On-the-fly optimization Cloudinary, Imgix

Example: Modern image optimization techniques

// Next.js Image component (automatic optimization)
import Image from 'next/image';

function Hero() {
  return (
    <Image
      src="/hero.jpg"
      alt="Hero image"
      width={1200}
      height={600}
      priority // Load immediately (above fold)
      placeholder="blur" // Show blur while loading
      blurDataURL="data:image/jpeg;base64,..." // Low-res placeholder
    />
  );
}

// Responsive images with next/image
<Image
  src="/product.jpg"
  alt="Product"
  width={800}
  height={600}
  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
  // Automatically generates srcset
/>

// External images (configure in next.config.js)
// next.config.js
module.exports = {
  images: {
    domains: ['cdn.example.com', 'images.unsplash.com'],
    formats: ['image/avif', 'image/webp'],
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
};

// Native responsive images with picture element
<picture>
  <source
    type="image/avif"
    srcSet="
      /image-small.avif 400w,
      /image-medium.avif 800w,
      /image-large.avif 1200w
    "
    sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
  />
  <source
    type="image/webp"
    srcSet="
      /image-small.webp 400w,
      /image-medium.webp 800w,
      /image-large.webp 1200w
    "
    sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
  />
  <img
    src="/image-large.jpg"
    alt="Description"
    loading="lazy"
    decoding="async"
  />
</picture>

// Native lazy loading
<img
  src="image.jpg"
  alt="Description"
  loading="lazy"
  decoding="async"
  width="800"
  height="600"
/>

// Blur placeholder with CSS
.image-container {
  background: linear-gradient(to bottom, #f0f0f0, #e0e0e0);
  position: relative;
}

.image-container::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url('data:image/jpeg;base64,...'); // Tiny base64
  background-size: cover;
  filter: blur(10px);
  transition: opacity 0.3s;
}

.image-container.loaded::before {
  opacity: 0;
}

// Convert images to WebP/AVIF with sharp
npm install sharp

// generateImages.js
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');

async function convertImage(inputPath, outputDir) {
  const filename = path.basename(inputPath, path.extname(inputPath));
  
  // Generate WebP
  await sharp(inputPath)
    .webp({ quality: 80 })
    .toFile(path.join(outputDir, `${filename}.webp`));
  
  // Generate AVIF
  await sharp(inputPath)
    .avif({ quality: 65 })
    .toFile(path.join(outputDir, `${filename}.avif`));
  
  // Generate responsive sizes
  const sizes = [400, 800, 1200];
  for (const size of sizes) {
    await sharp(inputPath)
      .resize(size)
      .webp({ quality: 80 })
      .toFile(path.join(outputDir, `${filename}-${size}.webp`));
  }
}

// Cloudinary integration
<img
  src="https://res.cloudinary.com/demo/image/upload/w_400,f_auto,q_auto/sample.jpg"
  alt="Optimized"
  loading="lazy"
/>
// w_400: width 400px
// f_auto: automatic format (WebP/AVIF)
// q_auto: automatic quality

// React component for optimized images
function OptimizedImage({ src, alt, width, height }) {
  const [loaded, setLoaded] = useState(false);

  return (
    <div className="image-wrapper">
      <picture>
        <source
          type="image/avif"
          srcSet={`${src}.avif`}
        />
        <source
          type="image/webp"
          srcSet={`${src}.webp`}
        />
        <img
          src={`${src}.jpg`}
          alt={alt}
          width={width}
          height={height}
          loading="lazy"
          decoding="async"
          onLoad={() => setLoaded(true)}
          className={loaded ? 'loaded' : ''}
        />
      </picture>
    </div>
  );
}

// Intersection Observer for custom lazy loading
function LazyImage({ src, alt }) {
  const [isLoaded, setIsLoaded] = useState(false);
  const [isInView, setIsInView] = useState(false);
  const imgRef = useRef();

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsInView(true);
          observer.disconnect();
        }
      },
      { rootMargin: '50px' }
    );

    if (imgRef.current) {
      observer.observe(imgRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <img
      ref={imgRef}
      src={isInView ? src : ''}
      alt={alt}
      onLoad={() => setIsLoaded(true)}
      style={{ opacity: isLoaded ? 1 : 0 }}
    />
  );
}

// Progressive JPEG loading
// Use progressive JPEG encoding for better perceived performance
// ImageMagick: convert input.jpg -interlace Plane output.jpg
// Sharp: sharp(input).jpeg({ progressive: true }).toFile(output)

// Image compression before upload
async function compressImage(file) {
  const options = {
    maxSizeMB: 1,
    maxWidthOrHeight: 1920,
    useWebWorker: true,
  };
  
  const compressedFile = await imageCompression(file, options);
  return compressedFile;
}

9.6 Service Worker Caching Workbox

Strategy Description Use Case Workbox Method
Cache First Cache, fallback to network Static assets (CSS, JS, images) CacheFirst
Network First Network, fallback to cache API calls, dynamic content NetworkFirst
Stale While Revalidate Cache first, update in background Frequent updates (avatars, feeds) StaleWhileRevalidate
Network Only Always use network Real-time data NetworkOnly
Cache Only Only use cache Pre-cached resources CacheOnly
Precaching Cache during install App shell, critical resources precacheAndRoute

Example: Service Worker with Workbox caching strategies

// Install Workbox
npm install workbox-webpack-plugin

// webpack.config.js
const { GenerateSW } = require('workbox-webpack-plugin');

module.exports = {
  plugins: [
    new GenerateSW({
      clientsClaim: true,
      skipWaiting: true,
      runtimeCaching: [
        {
          urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/,
          handler: 'CacheFirst',
          options: {
            cacheName: 'images',
            expiration: {
              maxEntries: 50,
              maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
            },
          },
        },
        {
          urlPattern: /^https:\/\/api\.example\.com/,
          handler: 'NetworkFirst',
          options: {
            cacheName: 'api-cache',
            networkTimeoutSeconds: 10,
            expiration: {
              maxEntries: 50,
              maxAgeSeconds: 5 * 60, // 5 minutes
            },
          },
        },
      ],
    }),
  ],
};

// Custom service worker with Workbox
// service-worker.js
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
import { CacheableResponsePlugin } from 'workbox-cacheable-response';

// Precache generated assets
precacheAndRoute(self.__WB_MANIFEST);

// Cache images with CacheFirst strategy
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'images',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 60,
        maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
      }),
    ],
  })
);

// Cache CSS and JavaScript with StaleWhileRevalidate
registerRoute(
  ({ request }) =>
    request.destination === 'style' ||
    request.destination === 'script',
  new StaleWhileRevalidate({
    cacheName: 'static-resources',
  })
);

// Cache API responses with NetworkFirst
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst({
    cacheName: 'api-cache',
    networkTimeoutSeconds: 10,
    plugins: [
      new CacheableResponsePlugin({
        statuses: [0, 200],
      }),
      new ExpirationPlugin({
        maxEntries: 50,
        maxAgeSeconds: 5 * 60, // 5 minutes
      }),
    ],
  })
);

// Cache Google Fonts
registerRoute(
  ({ url }) => url.origin === 'https://fonts.googleapis.com',
  new StaleWhileRevalidate({
    cacheName: 'google-fonts-stylesheets',
  })
);

registerRoute(
  ({ url }) => url.origin === 'https://fonts.gstatic.com',
  new CacheFirst({
    cacheName: 'google-fonts-webfonts',
    plugins: [
      new CacheableResponsePlugin({
        statuses: [0, 200],
      }),
      new ExpirationPlugin({
        maxAgeSeconds: 60 * 60 * 24 * 365, // 1 year
        maxEntries: 30,
      }),
    ],
  })
);

// Register service worker
// main.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker
      .register('/service-worker.js')
      .then(registration => {
        console.log('SW registered:', registration);
      })
      .catch(error => {
        console.log('SW registration failed:', error);
      });
  });
}

// Create React App with Workbox
// src/service-worker.js
import { clientsClaim } from 'workbox-core';
import { ExpirationPlugin } from 'workbox-expiration';
import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';

clientsClaim();

precacheAndRoute(self.__WB_MANIFEST);

const fileExtensionRegexp = new RegExp("/[^/?]+\\.[^/]+$");
registerRoute(
  ({ request, url }) => {
    if (request.mode !== 'navigate') return false;
    if (url.pathname.startsWith('/_')) return false;
    if (url.pathname.match(fileExtensionRegexp)) return false;
    return true;
  },
  createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
);

registerRoute(
  ({ url }) =>
    url.origin === self.location.origin && url.pathname.endsWith('.png'),
  new StaleWhileRevalidate({
    cacheName: 'images',
    plugins: [new ExpirationPlugin({ maxEntries: 50 })],
  })
);

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

// Register in index.js
import * as serviceWorkerRegistration from './serviceWorkerRegistration';

serviceWorkerRegistration.register();

// Next.js with next-pwa
npm install next-pwa

// next.config.js
const withPWA = require('next-pwa')({
  dest: 'public',
  register: true,
  skipWaiting: true,
  runtimeCaching: [
    {
      urlPattern: /^https:\/\/fonts\.(?:googleapis|gstatic)\.com\/.*/i,
      handler: 'CacheFirst',
      options: {
        cacheName: 'google-fonts',
        expiration: {
          maxEntries: 4,
          maxAgeSeconds: 365 * 24 * 60 * 60, // 365 days
        },
      },
    },
    {
      urlPattern: /\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,
      handler: 'StaleWhileRevalidate',
      options: {
        cacheName: 'static-font-assets',
        expiration: {
          maxEntries: 4,
          maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
        },
      },
    },
    {
      urlPattern: /\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,
      handler: 'StaleWhileRevalidate',
      options: {
        cacheName: 'static-image-assets',
        expiration: {
          maxEntries: 64,
          maxAgeSeconds: 24 * 60 * 60, // 24 hours
        },
      },
    },
  ],
});

module.exports = withPWA({
  // Next.js config
});

// Offline fallback page
// service-worker.js
import { offlineFallback } from 'workbox-recipes';

offlineFallback({
  pageFallback: '/offline.html',
});

// Background sync for failed requests
import { BackgroundSyncPlugin } from 'workbox-background-sync';

const bgSyncPlugin = new BackgroundSyncPlugin('api-queue', {
  maxRetentionTime: 24 * 60, // Retry for up to 24 hours (in minutes)
});

registerRoute(
  /\/api\/.*\/*.json/,
  new NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
  'POST'
);

// Cache analytics requests
import { Queue } from 'workbox-background-sync';

const queue = new Queue('analytics-queue');

self.addEventListener('fetch', (event) => {
  if (event.request.url.includes('/analytics')) {
    const promiseChain = fetch(event.request.clone()).catch(() => {
      return queue.pushRequest({ request: event.request });
    });

    event.waitUntil(promiseChain);
  }
});

Performance Optimization Summary

  • Core Web Vitals - Measure LCP (<2.5s), CLS (<0.1), FID/INP (<100ms/200ms) using web-vitals library, optimize with preload, proper dimensions, code splitting
  • Code Splitting - Use React.lazy + Suspense for route/component splitting, webpack magic comments for chunk naming, prefetch/preload for predictive loading
  • Memoization - React.memo for expensive component renders, useMemo for computed values, useCallback for function references passed to children
  • Bundle Analysis - Use webpack-bundle-analyzer or rollup visualizer, identify large dependencies, replace heavy libraries, tree shake unused code
  • Image Optimization - Use WebP/AVIF formats, next/image for automatic optimization, responsive images with srcset, lazy loading, blur placeholders
  • Service Workers - Implement caching strategies with Workbox: CacheFirst for static assets, NetworkFirst for API, StaleWhileRevalidate for frequently updated content
  • Best Practices - Set performance budgets, monitor with Lighthouse CI, compress assets (gzip/brotli), use CDN, minimize main thread work

10. Frontend Security Implementation Practices

10.1 XSS Prevention DOMPurify Sanitization

Attack Type Prevention Method Implementation Example
Stored XSS Sanitize on input/output DOMPurify, validator.js Clean user-generated content
Reflected XSS Escape user input Template literals, React escape URL parameters, search queries
DOM-based XSS Avoid dangerous APIs Avoid innerHTML, eval, document.write Use textContent, createElement
CSP Headers Restrict script sources Content-Security-Policy header Prevent inline scripts
HTTP-only Cookies Prevent JS access Set HttpOnly flag Protect session tokens
React dangerouslySetInnerHTML Sanitize before use DOMPurify + dangerouslySetInnerHTML Rich text editors

Example: XSS prevention with DOMPurify

// Install DOMPurify
npm install dompurify
npm install --save-dev @types/dompurify

// Sanitize user input with DOMPurify
import DOMPurify from 'dompurify';

function SafeHTML({ html }) {
  const cleanHTML = DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
    ALLOWED_ATTR: ['href', 'title', 'target'],
  });

  return (
    <div
      dangerouslySetInnerHTML={{ __html: cleanHTML }}
    />
  );
}

// Usage
const userContent = '<script>alert("XSS")</script><p>Safe content</p>';
<SafeHTML html={userContent} />
// Output: <p>Safe content</p> (script removed)

// React custom hook for sanitization
import { useMemo } from 'react';
import DOMPurify from 'dompurify';

function useSanitizedHTML(html: string) {
  return useMemo(() => DOMPurify.sanitize(html), [html]);
}

// Usage
function RichTextDisplay({ content }) {
  const cleanContent = useSanitizedHTML(content);
  
  return <div dangerouslySetInnerHTML={{ __html: cleanContent }} />;
}

// Strict sanitization (remove all HTML)
const strictClean = DOMPurify.sanitize(html, { ALLOWED_TAGS: [] });

// Custom sanitization with hooks
DOMPurify.addHook('afterSanitizeAttributes', (node) => {
  // Force all links to open in new tab
  if (node.tagName === 'A') {
    node.setAttribute('target', '_blank');
    node.setAttribute('rel', 'noopener noreferrer');
  }
});

// Avoid dangerous DOM APIs
// ❌ Dangerous - XSS vulnerable
element.innerHTML = userInput;
eval(userInput);
document.write(userInput);
new Function(userInput)();

// ✅ Safe alternatives
element.textContent = userInput;
element.innerText = userInput;
const node = document.createTextNode(userInput);
element.appendChild(node);

// React automatically escapes
function SafeComponent({ userInput }) {
  // React escapes this automatically
  return <div>{userInput}</div>;
}

// Validate and sanitize URL inputs
import validator from 'validator';

function validateURL(url: string) {
  if (!validator.isURL(url, { protocols: ['http', 'https'] })) {
    throw new Error('Invalid URL');
  }
  
  // Additional check for javascript: protocol
  if (url.toLowerCase().startsWith('javascript:')) {
    throw new Error('JavaScript URLs not allowed');
  }
  
  return url;
}

// Safe link component
function SafeLink({ href, children }) {
  const [isValid, setIsValid] = useState(false);

  useEffect(() => {
    try {
      validateURL(href);
      setIsValid(true);
    } catch {
      setIsValid(false);
    }
  }, [href]);

  if (!isValid) {
    return <span>{children}</span>;
  }

  return (
    <a
      href={href}
      target="_blank"
      rel="noopener noreferrer"
    >
      {children}
    </a>
  );
}

// Content Security Policy in Next.js
// next.config.js
const ContentSecurityPolicy = `
  default-src 'self';
  script-src 'self' 'unsafe-eval' 'unsafe-inline';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self' data:;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';
`;

module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim(),
          },
          {
            key: 'X-Frame-Options',
            value: 'DENY',
          },
          {
            key: 'X-Content-Type-Options',
            value: 'nosniff',
          },
          {
            key: 'Referrer-Policy',
            value: 'strict-origin-when-cross-origin',
          },
        ],
      },
    ];
  },
};

// Server-side sanitization (Node.js)
const express = require('express');
const { body, validationResult } = require('express-validator');
const DOMPurify = require('isomorphic-dompurify');

app.post('/comment',
  body('content').trim().escape(),
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const cleanContent = DOMPurify.sanitize(req.body.content);
    // Save cleanContent to database
  }
);

// Vue 3 safe rendering
<template>
  <!-- Safe by default -->
  <div>{{ userInput }}</div>
  
  <!-- Dangerous - sanitize first -->
  <div v-html="sanitizedHTML"></div>
</template>

<script setup>
import DOMPurify from 'dompurify';
import { computed } from 'vue';

const props = defineProps(['userInput']);

const sanitizedHTML = computed(() => 
  DOMPurify.sanitize(props.userInput)
);
</script>

// Angular safe HTML pipe
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import DOMPurify from 'dompurify';

@Pipe({ name: 'safeHtml' })
export class SafeHtmlPipe implements PipeTransform {
  constructor(private sanitizer: DomSanitizer) {}

  transform(html: string): SafeHtml {
    const cleanHTML = DOMPurify.sanitize(html);
    return this.sanitizer.bypassSecurityTrustHtml(cleanHTML);
  }
}

// Usage
<div [innerHTML]="userContent | safeHtml"></div>

10.2 CSRF Protection SameSite Cookies

Protection Method Implementation Description Browser Support
SameSite Cookie Set-Cookie: SameSite=Strict Prevent cross-site cookie sending All modern browsers
CSRF Token Synchronizer token pattern Unique token per session/request Universal
Double Submit Cookie Token in cookie + request header Compare values server-side Universal
Custom Header X-Requested-With: XMLHttpRequest CORS prevents cross-origin AJAX requests only
Origin Header Check Verify Origin/Referer header Validate request source Server-side validation

Example: CSRF protection implementation

// Set SameSite cookies (Express.js)
const express = require('express');
const session = require('express-session');

app.use(session({
  secret: process.env.SESSION_SECRET,
  cookie: {
    httpOnly: true,
    secure: true, // HTTPS only
    sameSite: 'strict', // or 'lax' for better UX
    maxAge: 24 * 60 * 60 * 1000, // 24 hours
  },
}));

// SameSite options:
// - Strict: Cookie not sent on any cross-site request
// - Lax: Cookie sent on top-level navigation (GET)
// - None: Cookie sent on all requests (requires Secure flag)

// CSRF Token with csurf middleware
const csrf = require('csurf');
const cookieParser = require('cookie-parser');

app.use(cookieParser());
app.use(csrf({ cookie: true }));

// Send token to client
app.get('/form', (req, res) => {
  res.render('form', { csrfToken: req.csrfToken() });
});

// Validate token on POST
app.post('/submit', (req, res) => {
  // Token automatically validated by middleware
  // If invalid, 403 error is thrown
  res.json({ success: true });
});

// React CSRF token implementation
// Store token in meta tag (server-rendered)
<head>
  <meta name="csrf-token" content="{{ csrfToken }}" />
</head>

// Read and include in requests
function getCSRFToken() {
  const meta = document.querySelector('meta[name="csrf-token"]');
  return meta ? meta.getAttribute('content') : '';
}

// Axios interceptor for CSRF token
import axios from 'axios';

axios.interceptors.request.use((config) => {
  const token = getCSRFToken();
  if (token) {
    config.headers['X-CSRF-Token'] = token;
  }
  return config;
});

// Fetch with CSRF token
async function submitForm(data) {
  const response = await fetch('/api/submit', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': getCSRFToken(),
    },
    body: JSON.stringify(data),
    credentials: 'same-origin', // Include cookies
  });

  return response.json();
}

// React hook for CSRF protection
function useCSRFToken() {
  const [token, setToken] = useState('');

  useEffect(() => {
    // Fetch token from server
    fetch('/api/csrf-token')
      .then(res => res.json())
      .then(data => setToken(data.token));
  }, []);

  return token;
}

// Usage
function SecureForm() {
  const csrfToken = useCSRFToken();

  const handleSubmit = async (data) => {
    await fetch('/api/submit', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': csrfToken,
      },
      body: JSON.stringify(data),
    });
  };

  return <form onSubmit={handleSubmit}>...</form>;
}

// Next.js API route with CSRF protection
// pages/api/submit.js
import { csrf } from 'lib/csrf';

export default csrf(async (req, res) => {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  // Token validated by middleware
  // Process request
  res.json({ success: true });
});

// Custom CSRF middleware
// lib/csrf.js
import { randomBytes } from 'crypto';

const tokens = new Map();

export function generateCSRFToken(sessionId) {
  const token = randomBytes(32).toString('hex');
  tokens.set(sessionId, token);
  return token;
}

export function validateCSRFToken(sessionId, token) {
  const storedToken = tokens.get(sessionId);
  return storedToken && storedToken === token;
}

export function csrf(handler) {
  return async (req, res) => {
    if (req.method === 'POST') {
      const token = req.headers['x-csrf-token'];
      const sessionId = req.cookies.sessionId;

      if (!validateCSRFToken(sessionId, token)) {
        return res.status(403).json({ error: 'Invalid CSRF token' });
      }
    }

    return handler(req, res);
  };
}

// Double Submit Cookie pattern
app.post('/api/submit', (req, res) => {
  const cookieToken = req.cookies.csrfToken;
  const headerToken = req.headers['x-csrf-token'];

  if (!cookieToken || cookieToken !== headerToken) {
    return res.status(403).json({ error: 'CSRF validation failed' });
  }

  // Process request
});

// Origin header validation
app.use((req, res, next) => {
  const origin = req.headers.origin || req.headers.referer;
  const allowedOrigins = [
    'https://example.com',
    'https://www.example.com',
  ];

  if (req.method !== 'GET' && req.method !== 'HEAD') {
    if (!origin || !allowedOrigins.some(allowed => origin.startsWith(allowed))) {
      return res.status(403).json({ error: 'Invalid origin' });
    }
  }

  next();
});

// Custom header check (AJAX only)
app.use((req, res, next) => {
  if (req.method !== 'GET' && req.method !== 'HEAD') {
    const xhr = req.headers['x-requested-with'] === 'XMLHttpRequest';
    if (!xhr) {
      return res.status(403).json({ error: 'Invalid request' });
    }
  }
  next();
});

// React Context for CSRF token
const CSRFContext = createContext('');

export function CSRFProvider({ children }) {
  const [token, setToken] = useState('');

  useEffect(() => {
    fetch('/api/csrf-token')
      .then(res => res.json())
      .then(data => setToken(data.token));
  }, []);

  return (
    <CSRFContext.Provider value={token}>
      {children}
    </CSRFContext.Provider>
  );
}

export const useCSRF = () => useContext(CSRFContext);

10.3 Content Security Policy Headers

Directive Purpose Example Value Protection
default-src Fallback for all directives 'self' Restrict all resources
script-src JavaScript sources 'self' 'nonce-{random}' Prevent inline scripts
style-src CSS sources 'self' 'unsafe-inline' Control stylesheets
img-src Image sources 'self' data: https: Restrict image loading
connect-src XHR, WebSocket, fetch 'self' https://api.example.com Control API endpoints
frame-ancestors Embedding pages 'none' Prevent clickjacking

Example: Comprehensive CSP implementation

// Strict CSP policy
Content-Security-Policy: 
  default-src 'self';
  script-src 'self' 'nonce-{random}';
  style-src 'self' 'nonce-{random}';
  img-src 'self' data: https:;
  font-src 'self' data:;
  connect-src 'self' https://api.example.com;
  frame-src 'none';
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';
  upgrade-insecure-requests;
  block-all-mixed-content;

// Express.js CSP middleware
const helmet = require('helmet');

app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "https:"],
      connectSrc: ["'self'", "https://api.example.com"],
      fontSrc: ["'self'", "data:"],
      objectSrc: ["'none'"],
      mediaSrc: ["'self'"],
      frameSrc: ["'none'"],
      frameAncestors: ["'none'"],
      upgradeInsecureRequests: [],
    },
  })
);

// Next.js CSP with nonce
// next.config.js
const crypto = require('crypto');

module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: generateCSP(),
          },
        ],
      },
    ];
  },
};

function generateCSP() {
  const nonce = crypto.randomBytes(16).toString('base64');
  
  const csp = {
    'default-src': ["'self'"],
    'script-src': ["'self'", `'nonce-${nonce}'`, "'strict-dynamic'"],
    'style-src': ["'self'", "'unsafe-inline'"],
    'img-src': ["'self'", "data:", "https:"],
    'font-src': ["'self'", "data:"],
    'connect-src': ["'self'", "https://api.example.com"],
    'frame-ancestors': ["'none'"],
    'base-uri': ["'self'"],
    'form-action': ["'self'"],
  };

  return Object.entries(csp)
    .map(([key, values]) => `${key} ${values.join(' ')}`)
    .join('; ');
}

// Use nonce in scripts
<script nonce="{nonce}">
  console.log('Allowed script');
</script>

// React Helmet for CSP
import { Helmet } from 'react-helmet';

function App() {
  return (
    <>
      <Helmet>
        <meta
          httpEquiv="Content-Security-Policy"
          content="default-src 'self'; script-src 'self' 'unsafe-inline'"
        />
      </Helmet>
      <div>App content</div>
    </>
  );
}

// CSP Report-Only mode (testing)
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

// CSP violation reporting endpoint
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
  console.log('CSP Violation:', req.body);
  // Log to monitoring service
  res.status(204).end();
});

// CSP violation handler (client-side)
document.addEventListener('securitypolicyviolation', (e) => {
  console.error('CSP Violation:', {
    blockedURI: e.blockedURI,
    violatedDirective: e.violatedDirective,
    originalPolicy: e.originalPolicy,
  });

  // Send to analytics
  fetch('/api/csp-violation', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      blockedURI: e.blockedURI,
      violatedDirective: e.violatedDirective,
    }),
  });
});

// Next.js 13 App Router with CSP
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
  
  const cspHeader = `
    default-src 'self';
    script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
    style-src 'self' 'nonce-${nonce}';
    img-src 'self' blob: data:;
    font-src 'self';
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    frame-ancestors 'none';
    upgrade-insecure-requests;
  `.replace(/\s{2,}/g, ' ').trim();

  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-nonce', nonce);
  requestHeaders.set('Content-Security-Policy', cspHeader);

  const response = NextResponse.next({
    request: {
      headers: requestHeaders,
    },
  });

  response.headers.set('Content-Security-Policy', cspHeader);
  return response;
}

// Vite CSP plugin
// vite.config.ts
import { defineConfig } from 'vite';
import { createHtmlPlugin } from 'vite-plugin-html';

export default defineConfig({
  plugins: [
    createHtmlPlugin({
      inject: {
        data: {
          csp: "default-src 'self'; script-src 'self' 'unsafe-inline'",
        },
      },
    }),
  ],
  server: {
    headers: {
      'Content-Security-Policy': "default-src 'self'; script-src 'self'",
    },
  },
});

// Additional security headers
app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  res.setHeader('Permissions-Policy', 'geolocation=(), microphone=()');
  next();
});

10.4 JWT Token HttpOnly Storage

Storage Method Security Level Pros Cons
HttpOnly Cookie 🟢 High XSS protection, automatic sending CSRF risk (mitigated with SameSite)
localStorage 🔴 Low Easy access, persists XSS vulnerable, no auto-sending
sessionStorage 🟡 Medium Tab-scoped, clears on close XSS vulnerable
Memory (React state) 🟢 High XSS resistant, cleared on refresh Lost on refresh, needs refresh token
Secure Cookie + CSRF Token 🟢 Highest XSS + CSRF protection Complex implementation

Example: Secure JWT token storage and handling

// ✅ RECOMMENDED: HttpOnly cookie with refresh token
// Server-side (Express.js)
const jwt = require('jsonwebtoken');

// Login endpoint
app.post('/auth/login', async (req, res) => {
  const { email, password } = req.body;
  
  // Validate credentials
  const user = await validateUser(email, password);
  
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Generate access token (short-lived)
  const accessToken = jwt.sign(
    { userId: user.id, email: user.email },
    process.env.ACCESS_TOKEN_SECRET,
    { expiresIn: '15m' }
  );

  // Generate refresh token (long-lived)
  const refreshToken = jwt.sign(
    { userId: user.id },
    process.env.REFRESH_TOKEN_SECRET,
    { expiresIn: '7d' }
  );

  // Store refresh token in database
  await storeRefreshToken(user.id, refreshToken);

  // Set HttpOnly cookies
  res.cookie('accessToken', accessToken, {
    httpOnly: true,
    secure: true, // HTTPS only
    sameSite: 'strict',
    maxAge: 15 * 60 * 1000, // 15 minutes
  });

  res.cookie('refreshToken', refreshToken, {
    httpOnly: true,
    secure: true,
    sameSite: 'strict',
    maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
    path: '/auth/refresh', // Only sent to refresh endpoint
  });

  res.json({ success: true, user: { id: user.id, email: user.email } });
});

// Verify token middleware
function authenticateToken(req, res, next) {
  const token = req.cookies.accessToken;

  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }

  jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
    if (err) {
      return res.status(403).json({ error: 'Invalid token' });
    }
    req.user = user;
    next();
  });
}

// Protected route
app.get('/api/profile', authenticateToken, (req, res) => {
  res.json({ user: req.user });
});

// Refresh token endpoint
app.post('/auth/refresh', async (req, res) => {
  const refreshToken = req.cookies.refreshToken;

  if (!refreshToken) {
    return res.status(401).json({ error: 'No refresh token' });
  }

  // Verify refresh token
  jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, async (err, user) => {
    if (err) {
      return res.status(403).json({ error: 'Invalid refresh token' });
    }

    // Check if token exists in database
    const storedToken = await getRefreshToken(user.userId);
    if (storedToken !== refreshToken) {
      return res.status(403).json({ error: 'Token revoked' });
    }

    // Generate new access token
    const newAccessToken = jwt.sign(
      { userId: user.userId },
      process.env.ACCESS_TOKEN_SECRET,
      { expiresIn: '15m' }
    );

    res.cookie('accessToken', newAccessToken, {
      httpOnly: true,
      secure: true,
      sameSite: 'strict',
      maxAge: 15 * 60 * 1000,
    });

    res.json({ success: true });
  });
});

// Logout endpoint
app.post('/auth/logout', async (req, res) => {
  const refreshToken = req.cookies.refreshToken;
  
  // Remove from database
  if (refreshToken) {
    await revokeRefreshToken(refreshToken);
  }

  // Clear cookies
  res.clearCookie('accessToken');
  res.clearCookie('refreshToken');
  
  res.json({ success: true });
});

// Client-side (React)
// Auth context with automatic refresh
import { createContext, useContext, useState, useEffect } from 'react';

const AuthContext = createContext(null);

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Check authentication on mount
    checkAuth();

    // Set up automatic token refresh
    const interval = setInterval(() => {
      refreshAccessToken();
    }, 14 * 60 * 1000); // Refresh every 14 minutes

    return () => clearInterval(interval);
  }, []);

  const checkAuth = async () => {
    try {
      const response = await fetch('/api/profile', {
        credentials: 'include', // Include cookies
      });

      if (response.ok) {
        const data = await response.json();
        setUser(data.user);
      }
    } catch (error) {
      console.error('Auth check failed:', error);
    } finally {
      setLoading(false);
    }
  };

  const refreshAccessToken = async () => {
    try {
      await fetch('/auth/refresh', {
        method: 'POST',
        credentials: 'include',
      });
    } catch (error) {
      console.error('Token refresh failed:', error);
      setUser(null);
    }
  };

  const login = async (email, password) => {
    const response = await fetch('/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password }),
      credentials: 'include',
    });

    if (response.ok) {
      const data = await response.json();
      setUser(data.user);
      return true;
    }

    return false;
  };

  const logout = async () => {
    await fetch('/auth/logout', {
      method: 'POST',
      credentials: 'include',
    });
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, login, logout, loading }}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => useContext(AuthContext);

// Axios interceptor for automatic token refresh
import axios from 'axios';

axios.defaults.withCredentials = true;

let isRefreshing = false;
let failedQueue = [];

const processQueue = (error, token = null) => {
  failedQueue.forEach(prom => {
    if (error) {
      prom.reject(error);
    } else {
      prom.resolve(token);
    }
  });
  
  failedQueue = [];
};

axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;

    if (error.response?.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        }).then(() => axios(originalRequest));
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        await axios.post('/auth/refresh');
        processQueue(null);
        return axios(originalRequest);
      } catch (refreshError) {
        processQueue(refreshError);
        // Redirect to login
        window.location.href = '/login';
        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
      }
    }

    return Promise.reject(error);
  }
);

// Next.js middleware for token validation
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { jwtVerify } from 'jose';

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('accessToken')?.value;

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  try {
    await jwtVerify(
      token,
      new TextEncoder().encode(process.env.ACCESS_TOKEN_SECRET!)
    );
    return NextResponse.next();
  } catch {
    return NextResponse.redirect(new URL('/login', request.url));
  }
}

export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*'],
};

10.5 Input Validation Joi Yup Schemas

Library Ecosystem Features Use Case
Joi Node.js backend Rich schema, custom messages API validation
Yup React frontend Schema validation, TypeScript Form validation
Zod Full-stack TypeScript Type inference, parse tRPC, type-safe APIs
express-validator Express.js Middleware chain Request validation
class-validator NestJS, TypeScript Decorator-based DTO validation
Ajv JSON Schema Fast, standard-based Configuration validation

Example: Comprehensive input validation

// Joi validation (Backend/Node.js)
npm install joi

const Joi = require('joi');

// Define schema
const userSchema = Joi.object({
  username: Joi.string()
    .alphanum()
    .min(3)
    .max(30)
    .required()
    .messages({
      'string.min': 'Username must be at least 3 characters',
      'any.required': 'Username is required',
    }),
  
  email: Joi.string()
    .email({ minDomainSegments: 2 })
    .required(),
  
  password: Joi.string()
    .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/)
    .required()
    .messages({
      'string.pattern.base': 'Password must contain uppercase, lowercase, number, and special character',
    }),
  
  age: Joi.number()
    .integer()
    .min(18)
    .max(120)
    .optional(),
  
  website: Joi.string()
    .uri()
    .optional(),
  
  birthdate: Joi.date()
    .iso()
    .max('now')
    .optional(),
  
  role: Joi.string()
    .valid('user', 'admin', 'moderator')
    .default('user'),
});

// Validate in Express route
app.post('/api/users', async (req, res) => {
  try {
    const value = await userSchema.validateAsync(req.body, {
      abortEarly: false, // Return all errors
    });
    
    // Value is validated and sanitized
    const user = await createUser(value);
    res.json(user);
  } catch (error) {
    if (error.isJoi) {
      return res.status(400).json({
        errors: error.details.map(detail => ({
          field: detail.path.join('.'),
          message: detail.message,
        })),
      });
    }
    res.status(500).json({ error: 'Internal server error' });
  }
});

// Yup validation (Frontend/React)
npm install yup

import * as yup from 'yup';

// Define schema
const loginSchema = yup.object({
  email: yup
    .string()
    .email('Invalid email address')
    .required('Email is required'),
  
  password: yup
    .string()
    .min(8, 'Password must be at least 8 characters')
    .matches(
      /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
      'Password must contain uppercase, lowercase, and number'
    )
    .required('Password is required'),
  
  rememberMe: yup.boolean().default(false),
});

// React Hook Form with Yup
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';

function LoginForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: yupResolver(loginSchema),
  });

  const onSubmit = async (data) => {
    // Data is validated
    await login(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email')} />
      {errors.email && <span>{errors.email.message}</span>}

      <input type="password" {...register('password')} />
      {errors.password && <span>{errors.password.message}</span>}

      <button type="submit">Login</button>
    </form>
  );
}

// Zod validation (Full-stack TypeScript)
npm install zod

import { z } from 'zod';

// Define schema with type inference
const userSchema = z.object({
  username: z.string().min(3).max(30),
  email: z.string().email(),
  password: z.string().min(8),
  age: z.number().int().min(18).optional(),
  role: z.enum(['user', 'admin', 'moderator']).default('user'),
});

// TypeScript type automatically inferred
type User = z.infer<typeof userSchema>;

// Validate
const result = userSchema.safeParse(data);

if (!result.success) {
  console.error(result.error.issues);
} else {
  const user: User = result.data;
}

// tRPC with Zod
import { router, publicProcedure } from './trpc';

export const userRouter = router({
  create: publicProcedure
    .input(userSchema)
    .mutation(async ({ input }) => {
      // input is typed as User
      return await createUser(input);
    }),
});

// express-validator
npm install express-validator

const { body, validationResult } = require('express-validator');

app.post('/api/users',
  // Validation middleware
  body('email').isEmail().normalizeEmail(),
  body('password').isLength({ min: 8 }),
  body('username').trim().isLength({ min: 3, max: 30 }).escape(),
  
  // Handler
  (req, res) => {
    const errors = validationResult(req);
    
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    
    // Process validated data
    const user = createUser(req.body);
    res.json(user);
  }
);

// Custom validation with Yup
const passwordMatchSchema = yup.object({
  password: yup.string().required(),
  confirmPassword: yup
    .string()
    .oneOf([yup.ref('password')], 'Passwords must match')
    .required(),
});

// Conditional validation
const addressSchema = yup.object({
  country: yup.string().required(),
  state: yup.string().when('country', {
    is: 'US',
    then: (schema) => schema.required('State is required for US'),
    otherwise: (schema) => schema.optional(),
  }),
});

// Array validation
const orderSchema = yup.object({
  items: yup.array().of(
    yup.object({
      productId: yup.string().required(),
      quantity: yup.number().positive().integer().required(),
      price: yup.number().positive().required(),
    })
  ).min(1, 'At least one item required'),
});

// File validation
const uploadSchema = yup.object({
  file: yup
    .mixed()
    .required('File is required')
    .test('fileSize', 'File too large', (value) => {
      return value && value.size <= 5000000; // 5MB
    })
    .test('fileType', 'Invalid file type', (value) => {
      return value && ['image/jpeg', 'image/png'].includes(value.type);
    }),
});

// Sanitization helpers
function sanitizeInput(input) {
  return input
    .trim()
    .replace(/[<>"'\/]/g, (char) => {
      const entities = {
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#x27;',
        '/': '&#x2F;',
      };
      return entities[char];
    });
}

// SQL injection prevention with parameterized queries
// ❌ Vulnerable
const query = `SELECT * FROM users WHERE email = '${email}'`;

// ✅ Safe
const query = 'SELECT * FROM users WHERE email = ?';
db.query(query, [email]);

// NoSQL injection prevention
// ❌ Vulnerable
const user = await User.findOne({ email: req.body.email });

// ✅ Safe with validation
const emailSchema = yup.string().email().required();
const email = await emailSchema.validate(req.body.email);
const user = await User.findOne({ email });

10.6 HTTPS Certificate Pinning Security

Security Measure Implementation Purpose Level
HTTPS/TLS SSL/TLS certificate Encrypt data in transit Essential
Certificate Pinning Pin public key/certificate Prevent MITM attacks Advanced
HSTS Strict-Transport-Security header Force HTTPS Recommended
TLS 1.3 Modern protocol version Latest encryption standards Best practice
Certificate Transparency CT logs monitoring Detect rogue certificates Advanced
Secure Ciphers Strong cipher suites Prevent weak encryption Essential

Example: HTTPS and security configuration

// HTTPS server setup (Node.js)
const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();

const options = {
  key: fs.readFileSync('/path/to/private-key.pem'),
  cert: fs.readFileSync('/path/to/certificate.pem'),
  ca: fs.readFileSync('/path/to/ca-certificate.pem'),
  
  // TLS options
  minVersion: 'TLSv1.3',
  ciphers: [
    'TLS_AES_128_GCM_SHA256',
    'TLS_AES_256_GCM_SHA384',
    'TLS_CHACHA20_POLY1305_SHA256',
  ].join(':'),
  honorCipherOrder: true,
};

https.createServer(options, app).listen(443);

// HSTS (HTTP Strict Transport Security)
app.use((req, res, next) => {
  res.setHeader(
    'Strict-Transport-Security',
    'max-age=31536000; includeSubDomains; preload'
  );
  next();
});

// Redirect HTTP to HTTPS
const http = require('http');

http.createServer((req, res) => {
  res.writeHead(301, {
    Location: `https://${req.headers.host}${req.url}`,
  });
  res.end();
}).listen(80);

// Next.js security headers
// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Strict-Transport-Security',
            value: 'max-age=63072000; includeSubDomains; preload',
          },
          {
            key: 'X-Content-Type-Options',
            value: 'nosniff',
          },
          {
            key: 'X-Frame-Options',
            value: 'SAMEORIGIN',
          },
          {
            key: 'X-XSS-Protection',
            value: '1; mode=block',
          },
          {
            key: 'Referrer-Policy',
            value: 'strict-origin-when-cross-origin',
          },
          {
            key: 'Permissions-Policy',
            value: 'camera=(), microphone=(), geolocation=()',
          },
        ],
      },
    ];
  },
};

// Helmet.js for Express security headers
npm install helmet

const helmet = require('helmet');

app.use(helmet({
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true,
  },
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'"],
    },
  },
  frameguard: {
    action: 'deny',
  },
}));

// Certificate pinning (React Native / Mobile)
// Note: Not commonly used in web browsers, mainly mobile apps
const fetch = require('node-fetch');
const https = require('https');
const crypto = require('crypto');

const expectedFingerprint = 'AA:BB:CC:DD:EE:FF...';

const agent = new https.Agent({
  checkServerIdentity: (hostname, cert) => {
    const fingerprint = crypto
      .createHash('sha256')
      .update(cert.raw)
      .digest('hex')
      .toUpperCase()
      .match(/.{2}/g)
      .join(':');

    if (fingerprint !== expectedFingerprint) {
      throw new Error('Certificate fingerprint mismatch');
    }
  },
});

fetch('https://api.example.com', { agent });

// Subresource Integrity (SRI) for CDN resources
<script
  src="https://cdn.example.com/library.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
  crossorigin="anonymous"
></script>

// Generate SRI hash
const crypto = require('crypto');
const fs = require('fs');

function generateSRI(filePath) {
  const content = fs.readFileSync(filePath);
  const hash = crypto.createHash('sha384').update(content).digest('base64');
  return `sha384-${hash}`;
}

// React component for SRI
function SecureScript({ src, integrity }) {
  useEffect(() => {
    const script = document.createElement('script');
    script.src = src;
    script.integrity = integrity;
    script.crossOrigin = 'anonymous';
    document.body.appendChild(script);

    return () => {
      document.body.removeChild(script);
    };
  }, [src, integrity]);

  return null;
}

// Nginx SSL configuration
server {
  listen 443 ssl http2;
  server_name example.com;

  ssl_certificate /path/to/certificate.crt;
  ssl_certificate_key /path/to/private.key;

  # TLS settings
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
  ssl_prefer_server_ciphers on;

  # HSTS
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

  # OCSP Stapling
  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_trusted_certificate /path/to/ca.crt;

  # Security headers
  add_header X-Frame-Options "SAMEORIGIN" always;
  add_header X-Content-Type-Options "nosniff" always;
  add_header X-XSS-Protection "1; mode=block" always;
}

// Let's Encrypt certificate automation
npm install greenlock-express

const greenlock = require('greenlock-express');

greenlock
  .init({
    packageRoot: __dirname,
    configDir: './greenlock.d',
    maintainerEmail: 'admin@example.com',
    cluster: false,
  })
  .serve(app);

// Certificate monitoring
const https = require('https');
const tls = require('tls');

function checkCertificate(hostname) {
  return new Promise((resolve, reject) => {
    const socket = tls.connect(443, hostname, () => {
      const cert = socket.getPeerCertificate();
      
      if (!cert || !Object.keys(cert).length) {
        reject(new Error('No certificate'));
        return;
      }

      const validTo = new Date(cert.valid_to);
      const daysRemaining = Math.floor((validTo - new Date()) / (1000 * 60 * 60 * 24));

      resolve({
        subject: cert.subject,
        issuer: cert.issuer,
        validFrom: cert.valid_from,
        validTo: cert.valid_to,
        daysRemaining,
      });

      socket.end();
    });

    socket.on('error', reject);
  });
}

// Usage
checkCertificate('example.com').then(cert => {
  console.log(`Certificate expires in ${cert.daysRemaining} days`);
  
  if (cert.daysRemaining < 30) {
    console.warn('Certificate expiring soon!');
    // Send alert
  }
});

Frontend Security Best Practices Summary

  • XSS Prevention - Use DOMPurify for sanitization, avoid innerHTML/eval/document.write, implement CSP headers, escape user input automatically (React/Vue)
  • CSRF Protection - Set SameSite cookies (Strict/Lax), implement CSRF tokens, validate Origin/Referer headers, use custom headers for AJAX
  • Content Security Policy - Define strict CSP directives, use nonces for inline scripts, implement CSP violation reporting, prevent inline script execution
  • JWT Security - Store tokens in HttpOnly cookies, implement refresh token rotation, use short-lived access tokens (15min), automatic token refresh mechanism
  • Input Validation - Use Joi/Yup/Zod schemas, validate on both client and server, sanitize inputs, use parameterized queries for SQL, prevent injection attacks
  • HTTPS & TLS - Enforce HTTPS with HSTS headers, use TLS 1.3, strong cipher suites, certificate monitoring, Subresource Integrity for CDN resources
  • Defense in Depth - Layer multiple security controls, use helmet.js for Express, implement rate limiting, monitor security violations, regular security audits

11. API Integration Modern Implementation

11.1 Axios Fetch API Error Handling

Feature Axios Fetch API Advantage
HTTP Client Third-party library Native browser API Fetch: No dependency
Error Handling Automatic error throwing Manual response.ok check Axios: Simpler errors
Request/Response Interceptors Built-in Manual wrapper needed Axios: More features
JSON Parsing Automatic Manual .json() call Axios: Convenience
Timeout Built-in timeout option AbortController needed Axios: Native timeout
Request Cancellation CancelToken / AbortController AbortController Both support cancellation

Example: Axios vs Fetch API with comprehensive error handling

// Install Axios
npm install axios

// Axios configuration with interceptors
import axios from 'axios';

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
});

// Request interceptor
apiClient.interceptors.request.use(
  (config) => {
    // Add auth token
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    
    // Add timestamp
    config.metadata = { startTime: new Date() };
    
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// Response interceptor
apiClient.interceptors.response.use(
  (response) => {
    // Log response time
    const endTime = new Date();
    const duration = endTime - response.config.metadata.startTime;
    console.log(`Request to ${response.config.url} took ${duration}ms`);
    
    return response.data;
  },
  async (error) => {
    const originalRequest = error.config;

    // Handle 401 - Refresh token
    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

      try {
        const { data } = await axios.post('/auth/refresh');
        localStorage.setItem('token', data.token);
        
        originalRequest.headers.Authorization = `Bearer ${data.token}`;
        return apiClient(originalRequest);
      } catch (refreshError) {
        // Redirect to login
        window.location.href = '/login';
        return Promise.reject(refreshError);
      }
    }

    // Handle different error types
    if (error.response) {
      // Server responded with error status
      console.error('Response error:', {
        status: error.response.status,
        data: error.response.data,
        headers: error.response.headers,
      });
    } else if (error.request) {
      // Request made but no response
      console.error('Network error:', error.request);
    } else {
      // Error in request setup
      console.error('Request setup error:', error.message);
    }

    return Promise.reject(error);
  }
);

// Usage examples
async function fetchUsers() {
  try {
    const users = await apiClient.get('/users');
    return users;
  } catch (error) {
    if (axios.isAxiosError(error)) {
      if (error.code === 'ECONNABORTED') {
        console.error('Request timeout');
      } else if (error.response?.status === 404) {
        console.error('Users not found');
      }
    }
    throw error;
  }
}

// Parallel requests
async function fetchDashboardData() {
  try {
    const [users, posts, comments] = await Promise.all([
      apiClient.get('/users'),
      apiClient.get('/posts'),
      apiClient.get('/comments'),
    ]);
    
    return { users, posts, comments };
  } catch (error) {
    console.error('Dashboard fetch failed:', error);
    throw error;
  }
}

// Request cancellation with Axios
const controller = new AbortController();

apiClient.get('/users', {
  signal: controller.signal,
})
.then(data => console.log(data))
.catch(error => {
  if (error.code === 'ERR_CANCELED') {
    console.log('Request cancelled');
  }
});

// Cancel the request
controller.abort();

// Fetch API with error handling
async function fetchWithErrorHandling(url, options = {}) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 10000);

  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal,
      headers: {
        'Content-Type': 'application/json',
        ...options.headers,
      },
    });

    clearTimeout(timeoutId);

    // Check for HTTP errors
    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    return data;
  } catch (error) {
    clearTimeout(timeoutId);

    if (error.name === 'AbortError') {
      throw new Error('Request timeout');
    }

    throw error;
  }
}

// Fetch wrapper with interceptors
class APIClient {
  constructor(baseURL) {
    this.baseURL = baseURL;
    this.requestInterceptors = [];
    this.responseInterceptors = [];
  }

  addRequestInterceptor(fn) {
    this.requestInterceptors.push(fn);
  }

  addResponseInterceptor(fn) {
    this.responseInterceptors.push(fn);
  }

  async request(endpoint, options = {}) {
    let url = `${this.baseURL}${endpoint}`;
    let config = { ...options };

    // Apply request interceptors
    for (const interceptor of this.requestInterceptors) {
      const result = await interceptor({ url, config });
      url = result.url;
      config = result.config;
    }

    try {
      const response = await fetch(url, config);

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      let data = await response.json();

      // Apply response interceptors
      for (const interceptor of this.responseInterceptors) {
        data = await interceptor(data);
      }

      return data;
    } catch (error) {
      console.error('Request failed:', error);
      throw error;
    }
  }

  get(endpoint, options) {
    return this.request(endpoint, { ...options, method: 'GET' });
  }

  post(endpoint, body, options) {
    return this.request(endpoint, {
      ...options,
      method: 'POST',
      body: JSON.stringify(body),
      headers: {
        'Content-Type': 'application/json',
        ...options?.headers,
      },
    });
  }
}

// Usage
const api = new APIClient('https://api.example.com');

api.addRequestInterceptor(({ url, config }) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers = {
      ...config.headers,
      Authorization: `Bearer ${token}`,
    };
  }
  return { url, config };
});

api.addResponseInterceptor((data) => {
  console.log('Response received:', data);
  return data;
});

// React hook for API calls
import { useState, useEffect } from 'react';

function useAPI(url, options = {}) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const controller = new AbortController();

    async function fetchData() {
      try {
        setLoading(true);
        const response = await apiClient.get(url, {
          ...options,
          signal: controller.signal,
        });
        setData(response);
        setError(null);
      } catch (err) {
        if (err.name !== 'CanceledError') {
          setError(err);
        }
      } finally {
        setLoading(false);
      }
    }

    fetchData();

    return () => controller.abort();
  }, [url]);

  return { data, loading, error };
}

// Usage
function UsersList() {
  const { data: users, loading, error } = useAPI('/users');

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

11.2 GraphQL Apollo Client Codegen

Feature Implementation Benefit Tool
Apollo Client GraphQL client library Caching, state management @apollo/client
Code Generation Types from schema Type safety, autocomplete graphql-codegen
Type-safe Hooks Generated React hooks Typed queries/mutations @graphql-codegen/typescript-react-apollo
Normalized Cache Automatic data normalization Efficient updates, consistency InMemoryCache
Optimistic Updates Update UI before response Better UX optimisticResponse option
Error Handling GraphQL + network errors Granular error info onError link

Example: GraphQL with Apollo Client and code generation

// Install dependencies
npm install @apollo/client graphql
npm install --save-dev @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo

// codegen.yml configuration
schema: 'https://api.example.com/graphql'
documents: 'src/**/*.graphql'
generates:
  src/generated/graphql.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo
    config:
      withHooks: true
      withHOC: false
      withComponent: false

// package.json scripts
{
  "scripts": {
    "codegen": "graphql-codegen --config codegen.yml",
    "codegen:watch": "graphql-codegen --config codegen.yml --watch"
  }
}

// GraphQL queries (src/queries/users.graphql)
query GetUsers {
  users {
    id
    name
    email
    avatar
  }
}

query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
    posts {
      id
      title
      content
    }
  }
}

mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    id
    name
    email
  }
}

mutation UpdateUser($id: ID!, $input: UpdateUserInput!) {
  updateUser(id: $id, input: $input) {
    id
    name
    email
  }
}

// Apollo Client setup
import { ApolloClient, InMemoryCache, ApolloProvider, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { onError } from '@apollo/client/link/error';

// HTTP link
const httpLink = createHttpLink({
  uri: 'https://api.example.com/graphql',
});

// Auth link
const authLink = setContext((_, { headers }) => {
  const token = localStorage.getItem('token');
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : '',
    },
  };
});

// Error link
const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, locations, path }) => {
      console.error(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`);
    });
  }

  if (networkError) {
    console.error(`[Network error]: ${networkError}`);
  }
});

// Create Apollo Client
const client = new ApolloClient({
  link: errorLink.concat(authLink.concat(httpLink)),
  cache: new InMemoryCache({
    typePolicies: {
      User: {
        keyFields: ['id'],
      },
      Post: {
        keyFields: ['id'],
      },
    },
  }),
  defaultOptions: {
    watchQuery: {
      fetchPolicy: 'cache-and-network',
      errorPolicy: 'all',
    },
    query: {
      fetchPolicy: 'network-only',
      errorPolicy: 'all',
    },
    mutate: {
      errorPolicy: 'all',
    },
  },
});

// App with ApolloProvider
function App() {
  return (
    <ApolloProvider client={client}>
      <YourApp />
    </ApolloProvider>
  );
}

// Use generated hooks (TypeScript)
import { useGetUsersQuery, useGetUserQuery, useCreateUserMutation } from './generated/graphql';

function UsersList() {
  const { data, loading, error, refetch } = useGetUsersQuery({
    pollInterval: 5000, // Poll every 5 seconds
  });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <button onClick={() => refetch()}>Refresh</button>
      <ul>
        {data?.users.map(user => (
          <li key={user.id}>{user.name} - {user.email}</li>
        ))}
      </ul>
    </div>
  );
}

// Single user with variables
function UserProfile({ userId }: { userId: string }) {
  const { data, loading, error } = useGetUserQuery({
    variables: { id: userId },
    skip: !userId, // Skip query if no userId
  });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!data?.user) return <div>User not found</div>;

  return (
    <div>
      <h1>{data.user.name}</h1>
      <p>{data.user.email}</p>
      <h2>Posts</h2>
      <ul>
        {data.user.posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

// Mutation with optimistic response
function CreateUserForm() {
  const [createUser, { loading, error }] = useCreateUserMutation({
    update(cache, { data }) {
      // Update cache after mutation
      cache.modify({
        fields: {
          users(existingUsers = []) {
            const newUserRef = cache.writeFragment({
              data: data?.createUser,
              fragment: gql`
                fragment NewUser on User {
                  id
                  name
                  email
                }
              `,
            });
            return [...existingUsers, newUserRef];
          },
        },
      });
    },
    optimisticResponse: {
      createUser: {
        __typename: 'User',
        id: 'temp-id',
        name: 'Loading...',
        email: 'loading@example.com',
      },
    },
  });

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const formData = new FormData(e.target as HTMLFormElement);

    try {
      await createUser({
        variables: {
          input: {
            name: formData.get('name') as string,
            email: formData.get('email') as string,
          },
        },
      });
    } catch (err) {
      console.error('Mutation error:', err);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="Name" required />
      <input name="email" type="email" placeholder="Email" required />
      <button type="submit" disabled={loading}>
        {loading ? 'Creating...' : 'Create User'}
      </button>
      {error && <div>Error: {error.message}</div>}
    </form>
  );
}

// Lazy query (load on demand)
import { useGetUserLazyQuery } from './generated/graphql';

function SearchUser() {
  const [getUser, { data, loading }] = useGetUserLazyQuery();

  const handleSearch = (userId: string) => {
    getUser({ variables: { id: userId } });
  };

  return (
    <div>
      <button onClick={() => handleSearch('123')}>Load User</button>
      {loading && <div>Loading...</div>}
      {data && <div>{data.user?.name}</div>}
    </div>
  );
}

// Subscription example
subscription OnUserCreated {
  userCreated {
    id
    name
    email
  }
}

// Use subscription
import { useOnUserCreatedSubscription } from './generated/graphql';

function RealtimeUsers() {
  const { data } = useOnUserCreatedSubscription();

  useEffect(() => {
    if (data?.userCreated) {
      console.log('New user created:', data.userCreated);
    }
  }, [data]);

  return <div>Listening for new users...</div>;
}

// Fragment colocation
const USER_FRAGMENT = gql`
  fragment UserFields on User {
    id
    name
    email
    avatar
  }
`;

const GET_USERS_WITH_FRAGMENT = gql`
  ${USER_FRAGMENT}
  query GetUsers {
    users {
      ...UserFields
    }
  }
`;

// Cache manipulation
import { useApolloClient } from '@apollo/client';

function UserActions() {
  const client = useApolloClient();

  const updateUserInCache = (userId: string, updates: Partial<User>) => {
    client.cache.modify({
      id: client.cache.identify({ __typename: 'User', id: userId }),
      fields: {
        name: () => updates.name,
        email: () => updates.email,
      },
    });
  };

  return <button onClick={() => updateUserInCache('1', { name: 'Updated' })}>Update</button>;
}

11.3 tRPC Type-safe API Calls

Feature Description Benefit Use Case
End-to-end Type Safety TypeScript from server to client No manual type definitions Full-stack TypeScript apps
No Code Generation Direct type inference Simpler setup vs GraphQL Monorepo projects
React Query Integration Built on @tanstack/react-query Caching, optimistic updates Modern React apps
Zod Validation Runtime input validation Type-safe validation Robust API contracts
Lightweight Small bundle size Better performance Bundle-conscious apps
Autocomplete Full IDE support Developer experience All TypeScript projects

Example: tRPC full-stack type-safe implementation

// Install tRPC
npm install @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query zod

// Server setup (server/trpc.ts)
import { initTRPC } from '@trpc/server';
import { z } from 'zod';

// Create tRPC context
export const createContext = async ({ req, res }) => {
  return {
    user: req.user,
    prisma: prisma, // or your database client
  };
};

type Context = Awaited<ReturnType<typeof createContext>>;

// Initialize tRPC
const t = initTRPC.context<Context>().create();

// Export reusable router and procedure helpers
export const router = t.router;
export const publicProcedure = t.procedure;

// Protected procedure with auth middleware
const isAuthed = t.middleware(({ ctx, next }) => {
  if (!ctx.user) {
    throw new Error('Not authenticated');
  }
  return next({
    ctx: {
      user: ctx.user,
    },
  });
});

export const protectedProcedure = t.procedure.use(isAuthed);

// Define router (server/routers/user.ts)
import { router, publicProcedure, protectedProcedure } from '../trpc';
import { z } from 'zod';

const userRouter = router({
  // Query - get all users
  getAll: publicProcedure
    .query(async ({ ctx }) => {
      return await ctx.prisma.user.findMany();
    }),

  // Query with input - get user by ID
  getById: publicProcedure
    .input(z.object({
      id: z.string(),
    }))
    .query(async ({ ctx, input }) => {
      return await ctx.prisma.user.findUnique({
        where: { id: input.id },
      });
    }),

  // Mutation - create user
  create: protectedProcedure
    .input(z.object({
      name: z.string().min(3).max(50),
      email: z.string().email(),
      age: z.number().int().min(18).optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      return await ctx.prisma.user.create({
        data: input,
      });
    }),

  // Mutation - update user
  update: protectedProcedure
    .input(z.object({
      id: z.string(),
      name: z.string().min(3).max(50).optional(),
      email: z.string().email().optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      const { id, ...data } = input;
      return await ctx.prisma.user.update({
        where: { id },
        data,
      });
    }),

  // Mutation - delete user
  delete: protectedProcedure
    .input(z.object({
      id: z.string(),
    }))
    .mutation(async ({ ctx, input }) => {
      return await ctx.prisma.user.delete({
        where: { id: input.id },
      });
    }),
});

// Main app router (server/routers/index.ts)
import { router } from '../trpc';
import { userRouter } from './user';
import { postRouter } from './post';

export const appRouter = router({
  user: userRouter,
  post: postRouter,
});

export type AppRouter = typeof appRouter;

// Next.js API route (pages/api/trpc/[trpc].ts)
import { createNextApiHandler } from '@trpc/server/adapters/next';
import { appRouter } from '../../../server/routers';
import { createContext } from '../../../server/trpc';

export default createNextApiHandler({
  router: appRouter,
  createContext,
  onError: ({ path, error }) => {
    console.error(`tRPC Error on '${path}':`, error);
  },
});

// Client setup (utils/trpc.ts)
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import type { AppRouter } from '../server/routers';

export const trpc = createTRPCNext<AppRouter>({
  config({ ctx }) {
    return {
      links: [
        httpBatchLink({
          url: '/api/trpc',
          headers() {
            return {
              authorization: `Bearer ${localStorage.getItem('token')}`,
            };
          },
        }),
      ],
      queryClientConfig: {
        defaultOptions: {
          queries: {
            staleTime: 60 * 1000, // 1 minute
          },
        },
      },
    };
  },
  ssr: false,
});

// Wrap app with tRPC provider (pages/_app.tsx)
import { trpc } from '../utils/trpc';
import type { AppProps } from 'next/app';

function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}

export default trpc.withTRPC(MyApp);

// Use tRPC in components - fully type-safe!
import { trpc } from '../utils/trpc';

function UsersList() {
  // Query - automatically typed!
  const { data: users, isLoading, error } = trpc.user.getAll.useQuery();

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <ul>
      {users?.map(user => (
        <li key={user.id}>{user.name} - {user.email}</li>
      ))}
    </ul>
  );
}

// Query with input
function UserProfile({ userId }: { userId: string }) {
  const { data: user } = trpc.user.getById.useQuery(
    { id: userId },
    { enabled: !!userId } // Only fetch if userId exists
  );

  return <div>{user?.name}</div>;
}

// Mutation
function CreateUserForm() {
  const utils = trpc.useContext();
  
  const createUser = trpc.user.create.useMutation({
    onSuccess: () => {
      // Invalidate and refetch users list
      utils.user.getAll.invalidate();
    },
    onError: (error) => {
      console.error('Failed to create user:', error);
    },
  });

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);

    createUser.mutate({
      name: formData.get('name') as string,
      email: formData.get('email') as string,
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" required />
      <input name="email" type="email" required />
      <button type="submit" disabled={createUser.isLoading}>
        {createUser.isLoading ? 'Creating...' : 'Create'}
      </button>
      {createUser.error && <div>{createUser.error.message}</div>}
    </form>
  );
}

// Optimistic updates
function UpdateUserForm({ userId }: { userId: string }) {
  const utils = trpc.useContext();

  const updateUser = trpc.user.update.useMutation({
    onMutate: async (newData) => {
      // Cancel outgoing refetches
      await utils.user.getById.cancel({ id: userId });

      // Snapshot previous value
      const previousUser = utils.user.getById.getData({ id: userId });

      // Optimistically update
      utils.user.getById.setData({ id: userId }, (old) => ({
        ...old!,
        ...newData,
      }));

      return { previousUser };
    },
    onError: (err, newData, context) => {
      // Rollback on error
      utils.user.getById.setData({ id: userId }, context?.previousUser);
    },
    onSettled: () => {
      // Refetch after error or success
      utils.user.getById.invalidate({ id: userId });
    },
  });

  return <div>...</div>;
}

// Prefetch for faster navigation
function UserLink({ userId }: { userId: string }) {
  const utils = trpc.useContext();

  const handleMouseEnter = () => {
    // Prefetch user data on hover
    utils.user.getById.prefetch({ id: userId });
  };

  return (
    <a href={`/users/${userId}`} onMouseEnter={handleMouseEnter}>
      View User
    </a>
  );
}

// Infinite query for pagination
const infiniteUserRouter = router({
  getInfinite: publicProcedure
    .input(z.object({
      limit: z.number().min(1).max(100).default(10),
      cursor: z.string().optional(),
    }))
    .query(async ({ ctx, input }) => {
      const users = await ctx.prisma.user.findMany({
        take: input.limit + 1,
        cursor: input.cursor ? { id: input.cursor } : undefined,
      });

      let nextCursor: string | undefined;
      if (users.length > input.limit) {
        const nextItem = users.pop();
        nextCursor = nextItem!.id;
      }

      return {
        users,
        nextCursor,
      };
    }),
});

// Use infinite query
function InfiniteUsersList() {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = trpc.user.getInfinite.useInfiniteQuery(
    { limit: 10 },
    {
      getNextPageParam: (lastPage) => lastPage.nextCursor,
    }
  );

  return (
    <div>
      {data?.pages.map((page, i) => (
        <div key={i}>
          {page.users.map(user => (
            <div key={user.id}>{user.name}</div>
          ))}
        </div>
      ))}
      
      {hasNextPage && (
        <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
          {isFetchingNextPage ? 'Loading...' : 'Load More'}
        </button>
      )}
    </div>
  );
}

11.4 React Query Infinite Queries

Feature Hook Purpose Use Case
useQuery Fetch data GET requests, caching List, detail views
useMutation Modify data POST/PUT/DELETE Forms, updates
useInfiniteQuery Paginated data Load more pattern Infinite scroll
Optimistic Updates Update before response Instant UI feedback Likes, votes, edits
Cache Invalidation Refetch stale data Keep data fresh After mutations
Prefetching Load data early Faster navigation Hover, route changes

Example: React Query comprehensive implementation

// Install React Query
npm install @tanstack/react-query @tanstack/react-query-devtools

// Setup QueryClient (App.tsx)
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60 * 1000,
      cacheTime: 5 * 60 * 1000,
      refetchOnWindowFocus: false,
      retry: 3,
    },
  },
});

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

// useQuery - Basic fetch
import { useQuery } from '@tanstack/react-query';

function UsersList() {
  const { data: users, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: () => fetch('/api/users').then(res => res.json()),
  });

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

// useMutation - POST/PUT/DELETE
import { useMutation, useQueryClient } from '@tanstack/react-query';

function CreateUserForm() {
  const queryClient = useQueryClient();

  const createUser = useMutation({
    mutationFn: (newUser) => {
      return fetch('/api/users', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newUser),
      }).then(res => res.json());
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });

  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      createUser.mutate({ name: 'John', email: 'john@example.com' });
    }}>
      <button type="submit">Create</button>
    </form>
  );
}

// useInfiniteQuery - Pagination
import { useInfiniteQuery } from '@tanstack/react-query';

function InfinitePostsList() {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery({
    queryKey: ['posts'],
    queryFn: ({ pageParam = 0 }) => {
      return fetch(`/api/posts?cursor=${pageParam}`).then(res => res.json());
    },
    getNextPageParam: (lastPage) => lastPage.nextCursor,
    initialPageParam: 0,
  });

  return (
    <div>
      {data?.pages.map((page, i) => (
        <div key={i}>
          {page.posts.map(post => (
            <div key={post.id}>{post.title}</div>
          ))}
        </div>
      ))}
      <button
        onClick={() => fetchNextPage()}
        disabled={!hasNextPage || isFetchingNextPage}
      >
        {isFetchingNextPage ? 'Loading...' : 'Load More'}
      </button>
    </div>
  );
}

// Optimistic Updates
function LikeButton({ postId }) {
  const queryClient = useQueryClient();

  const likeMutation = useMutation({
    mutationFn: (postId) => fetch(`/api/posts/${postId}/like`, { method: 'POST' }),
    onMutate: async (postId) => {
      await queryClient.cancelQueries({ queryKey: ['post', postId] });
      const previousPost = queryClient.getQueryData(['post', postId]);
      
      queryClient.setQueryData(['post', postId], (old) => ({
        ...old,
        likes: old.likes + 1,
      }));
      
      return { previousPost };
    },
    onError: (err, postId, context) => {
      queryClient.setQueryData(['post', postId], context.previousPost);
    },
  });

  return <button onClick={() => likeMutation.mutate(postId)}>Like</button>;
}

// Prefetch on hover
function PostLink({ postId }) {
  const queryClient = useQueryClient();

  return (
    <a
      href={`/posts/${postId}`}
      onMouseEnter={() => {
        queryClient.prefetchQuery({
          queryKey: ['post', postId],
          queryFn: () => fetch(`/api/posts/${postId}`).then(res => res.json()),
        });
      }}
    >
      View Post
    </a>
  );
}

11.5 MSW Mock Service Worker

Feature Purpose Environment Benefit
Service Worker API Intercept network requests Browser (dev/test) No code changes
Node.js Integration Mock in tests Jest, Vitest Test isolation
Type-safe Handlers TypeScript support All environments Catch errors early
REST & GraphQL Both API types Universal Flexible mocking
Stateful Mocking Simulate state changes Development Realistic behavior
Network Simulation Delays, errors Testing Edge case testing

Example: MSW for API mocking

// Install MSW
npm install msw --save-dev
npx msw init public/ --save

// Create handlers (src/mocks/handlers.ts)
import { http, HttpResponse, graphql } from 'msw';

export const handlers = [
  // REST handlers
  http.get('/api/users', () => {
    return HttpResponse.json([
      { id: '1', name: 'John Doe', email: 'john@example.com' },
      { id: '2', name: 'Jane Smith', email: 'jane@example.com' },
    ]);
  }),

  http.get('/api/users/:id', ({ params }) => {
    const { id } = params;
    return HttpResponse.json({
      id,
      name: 'John Doe',
      email: 'john@example.com',
    });
  }),

  http.post('/api/users', async ({ request }) => {
    const newUser = await request.json();
    return HttpResponse.json(
      { id: Math.random().toString(), ...newUser },
      { status: 201 }
    );
  }),

  // Simulate errors
  http.get('/api/error', () => {
    return HttpResponse.json(
      { error: 'Something went wrong' },
      { status: 500 }
    );
  }),

  // Simulate delay
  http.get('/api/slow', async () => {
    await delay(3000);
    return HttpResponse.json({ data: 'slow response' });
  }),

  // GraphQL handlers
  graphql.query('GetUsers', () => {
    return HttpResponse.json({
      data: {
        users: [
          { id: '1', name: 'John' },
          { id: '2', name: 'Jane' },
        ],
      },
    });
  }),
];

// Browser setup (src/mocks/browser.ts)
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';

export const worker = setupWorker(...handlers);

// Start in development (src/main.tsx)
async function enableMocking() {
  if (process.env.NODE_ENV !== 'development') return;

  const { worker } = await import('./mocks/browser');
  return worker.start();
}

enableMocking().then(() => {
  ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
});

// Test setup (src/mocks/server.ts)
import { setupServer } from 'msw/node';
import { handlers } from './handlers';

export const server = setupServer(...handlers);

// Setup tests (src/setupTests.ts)
import { beforeAll, afterEach, afterAll } from 'vitest';
import { server } from './mocks/server';

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

// Test with MSW
import { render, screen, waitFor } from '@testing-library/react';
import { server } from './mocks/server';
import { http, HttpResponse } from 'msw';

test('displays users', async () => {
  render(<UsersList />);
  
  await waitFor(() => {
    expect(screen.getByText('John Doe')).toBeInTheDocument();
  });
});

test('handles error', async () => {
  server.use(
    http.get('/api/users', () => {
      return HttpResponse.json({ error: 'Failed' }, { status: 500 });
    })
  );

  render(<UsersList />);
  await waitFor(() => {
    expect(screen.getByText(/error/i)).toBeInTheDocument();
  });
});

// Stateful mocking with database
import { factory, primaryKey } from '@mswjs/data';

const db = factory({
  user: {
    id: primaryKey(String),
    name: String,
    email: String,
  },
});

db.user.create({ id: '1', name: 'John', email: 'john@example.com' });

export const statefulHandlers = [
  http.get('/api/users', () => {
    return HttpResponse.json(db.user.getAll());
  }),
  
  http.post('/api/users', async ({ request }) => {
    const newUser = await request.json();
    const user = db.user.create({ id: Math.random().toString(), ...newUser });
    return HttpResponse.json(user, { status: 201 });
  }),
];

11.6 API Rate Limiting Retry Logic

Strategy Implementation Use Case Consideration
Exponential Backoff Delay *= 2 after each retry 429 rate limit errors Avoid thundering herd
Retry-After Header Respect server's retry time Server-specified delays Most accurate timing
Jitter Add randomness to delay Prevent synchronized retries Better distribution
Circuit Breaker Stop after threshold failures Protect failing services Fail fast when needed
Request Queue Throttle outgoing requests Client-side rate limiting Prevent hitting limits
Token Bucket Allow burst then limit Smooth traffic patterns Balance speed and limits

Example: Advanced retry logic and rate limiting

// Exponential backoff with jitter
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      if (response.ok) return response;

      if (response.status === 429 || response.status >= 500) {
        const retryAfter = response.headers.get('Retry-After');
        let delay;

        if (retryAfter) {
          delay = parseInt(retryAfter) * 1000;
        } else {
          const exponentialDelay = Math.pow(2, attempt) * 1000;
          const jitter = Math.random() * 1000;
          delay = exponentialDelay + jitter;
        }

        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      throw new Error(`HTTP ${response.status}`);
    } catch (error) {
      if (attempt === maxRetries) throw error;
    }
  }
}

// Circuit Breaker
class CircuitBreaker {
  constructor(threshold = 5, timeout = 60000) {
    this.failures = 0;
    this.threshold = threshold;
    this.timeout = timeout;
    this.state = 'CLOSED';
    this.lastFailureTime = null;
  }

  async execute(fn) {
    if (this.state === 'OPEN') {
      const timeSinceFailure = Date.now() - this.lastFailureTime;
      if (timeSinceFailure > this.timeout) {
        this.state = 'HALF_OPEN';
      } else {
        throw new Error('Circuit breaker is OPEN');
      }
    }

    try {
      const result = await fn();
      if (this.state === 'HALF_OPEN') {
        this.state = 'CLOSED';
        this.failures = 0;
      }
      return result;
    } catch (error) {
      this.failures++;
      this.lastFailureTime = Date.now();
      if (this.failures >= this.threshold) {
        this.state = 'OPEN';
      }
      throw error;
    }
  }
}

const breaker = new CircuitBreaker();
await breaker.execute(() => fetch('/api/data'));

// Request Queue with Rate Limiting
class RequestQueue {
  constructor(maxRequestsPerWindow = 10, windowMs = 1000) {
    this.queue = [];
    this.processing = false;
    this.requestsInWindow = 0;
    this.windowStart = Date.now();
    this.maxRequestsPerWindow = maxRequestsPerWindow;
    this.windowMs = windowMs;
  }

  async add(fn) {
    return new Promise((resolve, reject) => {
      this.queue.push(async () => {
        try {
          resolve(await fn());
        } catch (error) {
          reject(error);
        }
      });
      this.process();
    });
  }

  async process() {
    if (this.processing || this.queue.length === 0) return;
    this.processing = true;

    while (this.queue.length > 0) {
      const now = Date.now();
      const elapsed = now - this.windowStart;

      if (elapsed >= this.windowMs) {
        this.requestsInWindow = 0;
        this.windowStart = now;
      }

      if (this.requestsInWindow >= this.maxRequestsPerWindow) {
        await new Promise(resolve => setTimeout(resolve, this.windowMs - elapsed));
        continue;
      }

      const fn = this.queue.shift();
      this.requestsInWindow++;
      await fn();
    }

    this.processing = false;
  }
}

const queue = new RequestQueue(10, 1000);
for (let i = 0; i < 100; i++) {
  queue.add(() => fetch(`/api/data/${i}`));
}

// Token Bucket
class TokenBucket {
  constructor(capacity, refillRate) {
    this.tokens = capacity;
    this.capacity = capacity;
    this.refillRate = refillRate;
    this.lastRefill = Date.now();
  }

  async consume(tokens = 1) {
    this.refill();

    if (this.tokens >= tokens) {
      this.tokens -= tokens;
      return;
    }

    const tokensNeeded = tokens - this.tokens;
    const waitTime = (tokensNeeded / this.refillRate) * 1000;
    await new Promise(resolve => setTimeout(resolve, waitTime));
    
    this.refill();
    this.tokens -= tokens;
  }

  refill() {
    const now = Date.now();
    const elapsed = (now - this.lastRefill) / 1000;
    const tokensToAdd = elapsed * this.refillRate;
    this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
    this.lastRefill = now;
  }
}

const bucket = new TokenBucket(10, 2);
await bucket.consume(1);
await fetch('/api/data');

// React Query with retry
import { useQuery } from '@tanstack/react-query';

function UserData({ userId }) {
  const { data } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetch(`/api/users/${userId}`).then(res => res.json()),
    retry: (failureCount, error) => {
      if (error.response?.status === 404) return false;
      return failureCount < 3;
    },
    retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
  });

  return <div>{data?.name}</div>;
}

API Integration Best Practices Summary

  • Axios vs Fetch - Axios provides interceptors, automatic JSON parsing, built-in timeout; Fetch is native, needs manual error/timeout handling
  • GraphQL Apollo - Use @graphql-codegen for type-safe hooks, normalize cache, implement optimistic updates, leverage fragments
  • tRPC Type Safety - End-to-end TypeScript without codegen, Zod validation, built on React Query, automatic autocomplete
  • React Query Patterns - useQuery for fetching, useMutation for updates, useInfiniteQuery for pagination, optimistic updates, prefetch on hover
  • MSW Mocking - Mock APIs in browser with Service Worker, stateful mocking, test without backend, simulate delays/errors
  • Retry & Rate Limiting - Exponential backoff with jitter, respect Retry-After headers, circuit breaker, request queue, token bucket
  • Production Ready - Comprehensive error handling, automatic token refresh, request cancellation, proper TypeScript types, monitoring

12. Frontend Testing Implementation Stack

12.1 Jest Vitest Unit Testing Setup

Feature Jest Vitest Use Case
Configuration jest.config.js vitest.config.ts Test environment setup
Speed Standard Fast (Vite) Large test suites
Watch Mode --watch flag Built-in HMR Development workflow
Mocking jest.mock() vi.mock() Module mocking
Snapshot Testing toMatchSnapshot() toMatchSnapshot() UI regression
Coverage Built-in Istanbul c8 or Istanbul Code coverage reports

Example: Jest and Vitest comprehensive unit testing setup

// Jest Configuration (jest.config.js)
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
  moduleNameMapper: {
    "\\.(css|less|scss|sass)$": "identity-obj-proxy",
    "^@/(.*)$": "<rootDir>/src/$1",
  },
  collectCoverageFrom: [
    'src/**/*.{ts,tsx}',
    '!src/**/*.d.ts',
    '!src/main.tsx',
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80,
    },
  },
};

// Vitest Configuration (vitest.config.ts)
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/setupTests.ts',
    css: true,
    coverage: {
      provider: 'c8',
      reporter: ['text', 'json', 'html'],
      exclude: ['node_modules/', 'src/setupTests.ts'],
    },
  },
  resolve: {
    alias: {
      '@': '/src',
    },
  },
});

// Setup Tests (src/setupTests.ts)
import '@testing-library/jest-dom';
import { cleanup } from '@testing-library/react';
import { afterEach } from 'vitest';

afterEach(() => {
  cleanup();
});

// Basic Component Test
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { Button } from './Button';

describe('Button', () => {
  it('renders with text', () => {
    render(<Button>Click me</Button>);
    expect(screen.getByText('Click me')).toBeInTheDocument();
  });

  it('handles click events', async () => {
    const handleClick = vi.fn();
    render(<Button onClick={handleClick}>Click</Button>);
    
    const button = screen.getByRole('button');
    await userEvent.click(button);
    
    expect(handleClick).toHaveBeenCalledTimes(1);
  });

  it('is disabled when disabled prop is true', () => {
    render(<Button disabled>Disabled</Button>);
    expect(screen.getByRole('button')).toBeDisabled();
  });
});

// Async Test
import { waitFor } from '@testing-library/react';

describe('UserList', () => {
  it('fetches and displays users', async () => {
    render(<UserList />);
    
    expect(screen.getByText('Loading...')).toBeInTheDocument();
    
    await waitFor(() => {
      expect(screen.getByText('John Doe')).toBeInTheDocument();
    });
  });
});

// Mock Functions
import { vi } from 'vitest';

const mockFetch = vi.fn();
global.fetch = mockFetch;

mockFetch.mockResolvedValueOnce({
  json: async () => ({ name: 'John' }),
});

// Snapshot Testing
it('matches snapshot', () => {
  const { container } = render(<Card title="Test" />);
  expect(container.firstChild).toMatchSnapshot();
});

12.2 React Testing Library User Events

Method Purpose Async Use Case
userEvent.click() Simulate click Yes Buttons, links
userEvent.type() Simulate typing Yes Input fields
userEvent.clear() Clear input Yes Reset forms
userEvent.selectOptions() Select dropdown Yes Select elements
userEvent.upload() File upload Yes File inputs
userEvent.hover() Hover element Yes Tooltips, dropdowns

Example: React Testing Library comprehensive user interaction tests

// Install
npm install --save-dev @testing-library/react @testing-library/user-event @testing-library/jest-dom

// Form Testing
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { LoginForm } from './LoginForm';

describe('LoginForm', () => {
  it('submits form with user input', async () => {
    const user = userEvent.setup();
    const handleSubmit = vi.fn();
    
    render(<LoginForm onSubmit={handleSubmit} />);
    
    const emailInput = screen.getByLabelText(/email/i);
    const passwordInput = screen.getByLabelText(/password/i);
    const submitButton = screen.getByRole('button', { name: /login/i });
    
    await user.type(emailInput, 'test@example.com');
    await user.type(passwordInput, 'password123');
    await user.click(submitButton);
    
    expect(handleSubmit).toHaveBeenCalledWith({
      email: 'test@example.com',
      password: 'password123',
    });
  });

  it('shows validation errors', async () => {
    const user = userEvent.setup();
    render(<LoginForm />);
    
    const submitButton = screen.getByRole('button', { name: /login/i });
    await user.click(submitButton);
    
    expect(screen.getByText(/email is required/i)).toBeInTheDocument();
    expect(screen.getByText(/password is required/i)).toBeInTheDocument();
  });
});

// Query Methods
// getBy* - Throws error if not found (use for elements that should exist)
const button = screen.getByRole('button', { name: /submit/i });

// queryBy* - Returns null if not found (use for elements that shouldn't exist)
const error = screen.queryByText(/error/i);
expect(error).not.toBeInTheDocument();

// findBy* - Async, waits for element (use for elements that appear after async operations)
const message = await screen.findByText(/success/i);

// Select Dropdown Testing
it('selects option from dropdown', async () => {
  const user = userEvent.setup();
  render(<CountrySelector />);
  
  const select = screen.getByRole('combobox');
  await user.selectOptions(select, 'usa');
  
  expect(screen.getByRole('option', { name: 'USA' }).selected).toBe(true);
});

// File Upload Testing
it('uploads file', async () => {
  const user = userEvent.setup();
  const file = new File(['hello'], 'hello.png', { type: 'image/png' });
  
  render(<FileUpload />);
  
  const input = screen.getByLabelText(/upload file/i);
  await user.upload(input, file);
  
  expect(input.files[0]).toBe(file);
  expect(input.files).toHaveLength(1);
});

// Checkbox and Radio Testing
it('toggles checkbox', async () => {
  const user = userEvent.setup();
  render(<TermsCheckbox />);
  
  const checkbox = screen.getByRole('checkbox');
  expect(checkbox).not.toBeChecked();
  
  await user.click(checkbox);
  expect(checkbox).toBeChecked();
  
  await user.click(checkbox);
  expect(checkbox).not.toBeChecked();
});

// Hover and Focus Testing
it('shows tooltip on hover', async () => {
  const user = userEvent.setup();
  render(<TooltipButton />);
  
  const button = screen.getByRole('button');
  await user.hover(button);
  
  expect(await screen.findByRole('tooltip')).toBeInTheDocument();
  
  await user.unhover(button);
  expect(screen.queryByRole('tooltip')).not.toBeInTheDocument();
});

// Keyboard Navigation
it('navigates with keyboard', async () => {
  const user = userEvent.setup();
  render(<TabNavigation />);
  
  const firstTab = screen.getByRole('tab', { name: /first/i });
  firstTab.focus();
  
  await user.keyboard('{ArrowRight}');
  expect(screen.getByRole('tab', { name: /second/i })).toHaveFocus();
  
  await user.keyboard('{Enter}');
  expect(screen.getByRole('tabpanel')).toHaveTextContent('Second content');
});

// Custom Queries
import { within } from '@testing-library/react';

it('finds nested elements', () => {
  render(<UserCard />);
  
  const card = screen.getByRole('article');
  const heading = within(card).getByRole('heading');
  
  expect(heading).toHaveTextContent('John Doe');
});

12.3 Cypress Playwright E2E Automation

Feature Cypress Playwright Best For
Browser Support Chrome, Firefox, Edge All + Safari WebKit Cross-browser testing
Execution In-browser Node.js Speed and reliability
Auto-waiting Built-in Built-in Flaky test reduction
Debugging Time-travel UI Inspector, trace viewer Test development
Parallelization Paid (Dashboard) Free (built-in) CI/CD speed
Mobile Testing Viewport only Device emulation Mobile workflows

Example: Cypress and Playwright E2E testing

// Cypress Installation and Setup
npm install --save-dev cypress

// cypress.config.ts
import { defineConfig } from 'cypress';

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
    viewportWidth: 1280,
    viewportHeight: 720,
    video: false,
    screenshotOnRunFailure: true,
  },
});

// Cypress Test (cypress/e2e/login.cy.ts)
describe('Login Flow', () => {
  beforeEach(() => {
    cy.visit('/login');
  });

  it('logs in successfully', () => {
    cy.get('[data-testid="email-input"]').type('test@example.com');
    cy.get('[data-testid="password-input"]').type('password123');
    cy.get('[data-testid="login-button"]').click();
    
    cy.url().should('include', '/dashboard');
    cy.contains('Welcome back').should('be.visible');
  });

  it('shows error for invalid credentials', () => {
    cy.get('[data-testid="email-input"]').type('wrong@example.com');
    cy.get('[data-testid="password-input"]').type('wrong');
    cy.get('[data-testid="login-button"]').click();
    
    cy.contains('Invalid credentials').should('be.visible');
  });
});

// Cypress Custom Commands (cypress/support/commands.ts)
Cypress.Commands.add('login', (email, password) => {
  cy.session([email, password], () => {
    cy.visit('/login');
    cy.get('[data-testid="email-input"]').type(email);
    cy.get('[data-testid="password-input"]').type(password);
    cy.get('[data-testid="login-button"]').click();
    cy.url().should('include', '/dashboard');
  });
});

// Use custom command
it('accesses protected page', () => {
  cy.login('test@example.com', 'password123');
  cy.visit('/profile');
  cy.contains('Profile').should('be.visible');
});

// Playwright Installation and Setup
npm install --save-dev @playwright/test

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
});

// Playwright Test (tests/login.spec.ts)
import { test, expect } from '@playwright/test';

test.describe('Login Flow', () => {
  test('logs in successfully', async ({ page }) => {
    await page.goto('/login');
    
    await page.fill('[data-testid="email-input"]', 'test@example.com');
    await page.fill('[data-testid="password-input"]', 'password123');
    await page.click('[data-testid="login-button"]');
    
    await expect(page).toHaveURL(/.*dashboard/);
    await expect(page.locator('text=Welcome back')).toBeVisible();
  });

  test('handles network errors', async ({ page }) => {
    await page.route('**/api/login', route => route.abort());
    
    await page.goto('/login');
    await page.fill('[data-testid="email-input"]', 'test@example.com');
    await page.fill('[data-testid="password-input"]', 'password123');
    await page.click('[data-testid="login-button"]');
    
    await expect(page.locator('text=Network error')).toBeVisible();
  });
});

// Playwright Fixtures (Custom Setup)
import { test as base } from '@playwright/test';

type MyFixtures = {
  authenticatedPage: Page;
};

export const test = base.extend<MyFixtures>({
  authenticatedPage: async ({ page }, use) => {
    await page.goto('/login');
    await page.fill('[data-testid="email-input"]', 'test@example.com');
    await page.fill('[data-testid="password-input"]', 'password123');
    await page.click('[data-testid="login-button"]');
    await page.waitForURL('**/dashboard');
    await use(page);
  },
});

// Use fixture
test('accesses profile', async ({ authenticatedPage }) => {
  await authenticatedPage.goto('/profile');
  await expect(authenticatedPage.locator('h1')).toContainText('Profile');
});

12.4 Storybook Component Testing

Feature Purpose Benefit Use Case
Component Isolation Develop in isolation Focus on single component UI development
Stories Component states Document all variants Design system
Controls Interactive props Live editing Testing variations
Actions Event logging Track interactions Event handlers
Docs Auto-generated docs Component documentation Team collaboration
Addons Extend functionality A11y, testing, design Enhanced workflow

Example: Storybook setup and component stories

// Install Storybook
npx storybook@latest init

// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/addon-a11y',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
};

export default config;

// Button Component (src/components/Button.tsx)
import React from 'react';

interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'sm' | 'md' | 'lg';
  children: React.ReactNode;
  onClick?: () => void;
  disabled?: boolean;
}

export const Button: React.FC<ButtonProps> = ({
  variant = 'primary',
  size = 'md',
  children,
  onClick,
  disabled,
}) => {
  return (
    <button
      className={`btn btn-${variant} btn-${size}`}
      onClick={onClick}
      disabled={disabled}
    >
      {children}
    </button>
  );
};

// Button Stories (src/components/Button.stories.tsx)
import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
  parameters: {
    layout: 'centered',
  },
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'danger'],
    },
    size: {
      control: 'select',
      options: ['sm', 'md', 'lg'],
    },
  },
  args: {
    onClick: fn(),
  },
};

export default meta;
type Story = StoryObj<typeof Button>;

export const Primary: Story = {
  args: {
    variant: 'primary',
    children: 'Primary Button',
  },
};

export const Secondary: Story = {
  args: {
    variant: 'secondary',
    children: 'Secondary Button',
  },
};

export const Danger: Story = {
  args: {
    variant: 'danger',
    children: 'Danger Button',
  },
};

export const Small: Story = {
  args: {
    size: 'sm',
    children: 'Small Button',
  },
};

export const Large: Story = {
  args: {
    size: 'lg',
    children: 'Large Button',
  },
};

export const Disabled: Story = {
  args: {
    disabled: true,
    children: 'Disabled Button',
  },
};

// Interaction Testing
import { within, userEvent } from '@storybook/test';

export const WithInteractions: Story = {
  args: {
    children: 'Click me',
  },
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    const button = canvas.getByRole('button');
    
    await userEvent.click(button);
  },
};

// Complex Story with Decorators
export const WithContext: Story = {
  args: {
    children: 'Themed Button',
  },
  decorators: [
    (Story) => (
      <div style={{ padding: '3rem', background: '#f0f0f0' }}>
        <Story />
      </div>
    ),
  ],
};

// Form Story (src/components/LoginForm.stories.tsx)
import { LoginForm } from './LoginForm';

const meta: Meta<typeof LoginForm> = {
  title: 'Forms/LoginForm',
  component: LoginForm,
};

export default meta;

export const Default: Story = {};

export const WithError: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    
    await userEvent.click(canvas.getByRole('button', { name: /login/i }));
    
    // Verify error messages appear
    await canvas.findByText(/email is required/i);
  },
};

export const SuccessfulLogin: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    
    await userEvent.type(
      canvas.getByLabelText(/email/i),
      'test@example.com'
    );
    await userEvent.type(
      canvas.getByLabelText(/password/i),
      'password123'
    );
    await userEvent.click(canvas.getByRole('button', { name: /login/i }));
  },
};

12.5 Istanbul Coverage.js Test Coverage

Metric Definition Threshold Importance
Line Coverage % of lines executed 80-90% Basic coverage metric
Branch Coverage % of if/else branches 75-85% Logic coverage
Function Coverage % of functions called 80-90% API coverage
Statement Coverage % of statements executed 80-90% Code execution
Uncovered Lines Lines not executed Minimize Gap identification
Coverage Reports HTML/JSON/LCOV CI integration Tracking trends

Example: Istanbul coverage configuration and reporting

// Jest with Coverage (package.json)
{
  "scripts": {
    "test": "jest",
    "test:coverage": "jest --coverage",
    "test:coverage:watch": "jest --coverage --watchAll"
  },
  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,jsx,ts,tsx}",
      "!src/**/*.d.ts",
      "!src/**/*.stories.tsx",
      "!src/main.tsx",
      "!src/vite-env.d.ts"
    ],
    "coverageThreshold": {
      "global": {
        "branches": 80,
        "functions": 80,
        "lines": 80,
        "statements": 80
      },
      "src/components/": {
        "branches": 90,
        "functions": 90,
        "lines": 90,
        "statements": 90
      }
    },
    "coverageReporters": [
      "text",
      "text-summary",
      "html",
      "lcov",
      "json"
    ],
    "coveragePathIgnorePatterns": [
      "/node_modules/",
      "/dist/",
      "/coverage/"
    ]
  }
}

// Vitest with c8 Coverage (vitest.config.ts)
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    coverage: {
      provider: 'c8',
      reporter: ['text', 'json', 'html', 'lcov'],
      exclude: [
        'node_modules/',
        'src/setupTests.ts',
        '**/*.d.ts',
        '**/*.config.ts',
        '**/mockData',
        '**/*.stories.tsx',
      ],
      all: true,
      lines: 80,
      functions: 80,
      branches: 80,
      statements: 80,
      perFile: true,
    },
  },
});

// GitHub Actions CI with Coverage (..github/workflows/test.yml)
name: Test Coverage

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Run tests with coverage
        run: npm run test:coverage
        
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/lcov.info
          flags: unittests
          name: codecov-umbrella
          
      - name: Comment PR with coverage
        uses: romeovs/lcov-reporter-action@v0.3.1
        with:
          lcov-file: ./coverage/lcov.info
          github-token: ${{ secrets.GITHUB_TOKEN }}

// Custom Coverage Script (scripts/coverage-check.js)
const fs = require('fs');
const path = require('path');

const coverageSummary = JSON.parse(
  fs.readFileSync(path.join(__dirname, '../coverage/coverage-summary.json'), 'utf8')
);

const thresholds = {
  lines: 80,
  statements: 80,
  functions: 80,
  branches: 80,
};

let failed = false;

Object.entries(thresholds).forEach(([metric, threshold]) => {
  const coverage = coverageSummary.total[metric].pct;
  console.log(`${metric}: ${coverage}% (threshold: ${threshold}%)`);
  
  if (coverage < threshold) {
    console.error(`❌ ${metric} coverage ${coverage}% is below threshold ${threshold}%`);
    failed = true;
  } else {
    console.log(`✅ ${metric} coverage passed`);
  }
});

if (failed) {
  process.exit(1);
}

// Exclude files from coverage
/* istanbul ignore next */
function debugOnlyFunction() {
  console.log('Debug info');
}

// Ignore specific lines
/* istanbul ignore next */
if (process.env.NODE_ENV === 'development') {
  console.log('Dev mode');
}

// Ignore branches
/* istanbul ignore else */
if (condition) {
  doSomething();
}

// Package.json scripts for coverage gates
{
  "scripts": {
    "test:coverage": "vitest run --coverage",
    "test:coverage:check": "npm run test:coverage && node scripts/coverage-check.js",
    "precommit": "npm run test:coverage:check"
  }
}

12.6 Visual Regression Testing Chromatic

Tool Method Platform Use Case
Chromatic Cloud-based snapshots Storybook integration UI review workflow
Percy Visual diffs CI/CD integration Pull request reviews
BackstopJS Screenshot comparison Headless browser Local testing
Playwright Screenshots Built-in snapshots Test suite E2E visual testing
Jest Image Snapshot Image comparison Unit tests Component snapshots
Applitools AI-powered diffs Cross-browser Enterprise testing

Example: Visual regression testing setup

// Chromatic Setup
npm install --save-dev chromatic

// package.json
{
  "scripts": {
    "chromatic": "chromatic --project-token=<your-project-token>"
  }
}

// .github/workflows/chromatic.yml
name: Chromatic

on: push

jobs:
  visual-regression:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
          
      - name: Install dependencies
        run: npm ci
        
      - name: Publish to Chromatic
        uses: chromaui/action@v1
        with:
          projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
          autoAcceptChanges: main

// Playwright Visual Regression
import { test, expect } from '@playwright/test';

test('homepage visual regression', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveScreenshot('homepage.png');
});

test('button states visual regression', async ({ page }) => {
  await page.goto('/components/button');
  
  const button = page.locator('[data-testid="primary-button"]');
  await expect(button).toHaveScreenshot('button-default.png');
  
  await button.hover();
  await expect(button).toHaveScreenshot('button-hover.png');
  
  await button.focus();
  await expect(button).toHaveScreenshot('button-focus.png');
});

// Playwright Config for Visual Tests
import { defineConfig } from '@playwright/test';

export default defineConfig({
  expect: {
    toHaveScreenshot: {
      maxDiffPixels: 100,
      threshold: 0.2,
    },
  },
  use: {
    screenshot: 'only-on-failure',
  },
});

// Update screenshots command
// npx playwright test --update-snapshots

// BackstopJS Configuration (backstop.json)
{
  "id": "frontend_visual_test",
  "viewports": [
    {
      "label": "desktop",
      "width": 1280,
      "height": 720
    },
    {
      "label": "tablet",
      "width": 768,
      "height": 1024
    },
    {
      "label": "mobile",
      "width": 375,
      "height": 667
    }
  ],
  "scenarios": [
    {
      "label": "Homepage",
      "url": "http://localhost:3000",
      "referenceUrl": "",
      "readyEvent": "",
      "readySelector": "",
      "delay": 500,
      "hideSelectors": [],
      "removeSelectors": [],
      "hoverSelector": "",
      "clickSelector": "",
      "postInteractionWait": 0,
      "selectors": ["document"],
      "selectorExpansion": true,
      "misMatchThreshold": 0.1
    },
    {
      "label": "Button Hover",
      "url": "http://localhost:3000/components",
      "hoverSelector": ".btn-primary",
      "delay": 200
    }
  ],
  "paths": {
    "bitmaps_reference": "backstop_data/bitmaps_reference",
    "bitmaps_test": "backstop_data/bitmaps_test",
    "engine_scripts": "backstop_data/engine_scripts",
    "html_report": "backstop_data/html_report",
    "ci_report": "backstop_data/ci_report"
  },
  "report": ["browser", "CI"],
  "engine": "puppeteer"
}

// BackstopJS Commands
// Reference (baseline): npx backstop reference
// Test: npx backstop test
// Approve changes: npx backstop approve

// Jest Image Snapshot
npm install --save-dev jest-image-snapshot

// setupTests.ts
import { toMatchImageSnapshot } from 'jest-image-snapshot';

expect.extend({ toMatchImageSnapshot });

// Component visual test
import { render } from '@testing-library/react';
import { Button } from './Button';

it('matches visual snapshot', () => {
  const { container } = render(<Button>Click me</Button>);
  
  expect(container.firstChild).toMatchImageSnapshot({
    customSnapshotsDir: '__image_snapshots__',
    customDiffDir: '__image_snapshots__/__diff_output__',
    failureThreshold: 0.01,
    failureThresholdType: 'percent',
  });
});

// Percy Configuration (.percy.yml)
version: 2
static:
  files: '**/*.html'
  ignore-files: '**/node_modules/**'
snapshot:
  widths:
    - 375
    - 768
    - 1280
  min-height: 1024
  percy-css: |
    .animation { animation: none !important; }
    .transition { transition: none !important; }

// Percy with Storybook
npx percy storybook http://localhost:6006

Frontend Testing Best Practices Summary

  • Unit Testing - Jest/Vitest for fast unit tests, mock dependencies, 80%+ coverage, test pure functions and business logic
  • Component Testing - React Testing Library with userEvent for realistic user interactions, avoid implementation details, query by role/label
  • E2E Testing - Cypress for quick setup with time-travel debugging, Playwright for multi-browser with built-in parallelization
  • Storybook - Develop components in isolation, document all states, interaction testing, visual testing with Chromatic
  • Coverage Metrics - Track line, branch, function, statement coverage; set thresholds per directory; integrate with CI/CD
  • Visual Regression - Chromatic for Storybook, Playwright screenshots, Percy for PR reviews, catch unintended UI changes
  • Testing Strategy - Testing pyramid: many unit tests, some integration tests, few E2E tests; fast feedback, reliable CI

13. Modern Styling Implementation Patterns

13.1 CSS-in-JS Styled-components Emotion

CSS-in-JS libraries enable writing CSS directly in JavaScript with dynamic styling, scoped styles, and TypeScript support.

Library Features Use Case Bundle Impact
styled-components Tagged templates, theming, SSR, dynamic props, attrs React apps with dynamic theming ~15KB gzipped
Emotion css prop, styled API, composition, framework agnostic Performance-critical apps ~7KB gzipped
Styled JSX Next.js built-in, scoped styles, zero runtime Next.js projects Build-time only
Linaria Zero-runtime, CSS extraction, static styles Performance-first projects 0KB runtime

Example: Styled-components with dynamic theming

// theme.ts
export const theme = {
  colors: {
    primary: '#007acc',
    secondary: '#6c757d',
    danger: '#dc3545'
  },
  spacing: (n: number) => `${n * 8}px`
};

// Button.tsx
import styled from 'styled-components';

const Button = styled.button<{ variant?: 'primary' | 'secondary' }>`
  padding: ${props => props.theme.spacing(2)};
  background: ${props => props.theme.colors[props.variant || 'primary']};
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  
  &:hover {
    opacity: 0.9;
  }
  
  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
`;

// App.tsx
import { ThemeProvider } from 'styled-components';

function App() {
  return (
    <ThemeProvider theme={theme}>
      <Button variant="primary">Submit</Button>
      <Button variant="secondary">Cancel</Button>
    </ThemeProvider>
  );
}

Example: Emotion with css prop (faster than styled-components)

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

const buttonStyle = css`
  padding: 12px 24px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  border-radius: 8px;
  font-weight: 600;
  transition: transform 0.2s;
  
  &:hover {
    transform: translateY(-2px);
  }
`;

function Button({ children }) {
  return <button css={buttonStyle}>{children}</button>;
}

// Composition
const primaryButton = css`
  ${buttonStyle};
  background: #007acc;
`;

const dangerButton = css`
  ${buttonStyle};
  background: #dc3545;
`;
Note: Styled-components offers better DX with DevTools and component names in DOM. Emotion is 50% smaller and faster but requires css prop setup. Linaria for zero-runtime SSG sites.

13.2 Tailwind CSS Utility-first Design

Utility-first CSS framework for rapid UI development with consistent design system and minimal custom CSS.

Category Utilities Example Output CSS
Layout flex, grid, container, box-sizing flex items-center justify-between display: flex; align-items: center; justify-content: space-between
Spacing p-*, m-*, space-*, gap-* p-4 m-2 gap-6 padding: 1rem; margin: 0.5rem; gap: 1.5rem
Typography text-*, font-*, leading-*, tracking-* text-xl font-bold text-gray-900 font-size: 1.25rem; font-weight: 700; color: #111827
Colors bg-*, text-*, border-*, from-*, to-* bg-blue-500 text-white background: #3b82f6; color: #ffffff
Responsive sm:*, md:*, lg:*, xl:*, 2xl:* sm:w-full md:w-1/2 lg:w-1/3 Media query breakpoints: 640px, 768px, 1024px, 1280px, 1536px
States hover:*, focus:*, active:*, disabled:* hover:bg-blue-600 focus:ring-2 Pseudo-class variants

Example: Tailwind configuration and custom utilities

// tailwind.config.js
module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    extend: {
      colors: {
        brand: {
          50: '#f0f9ff',
          500: '#0ea5e9',
          900: '#0c4a6e'
        }
      },
      spacing: {
        '18': '4.5rem',
        '88': '22rem'
      },
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif']
      }
    }
  },
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
    require('@tailwindcss/aspect-ratio')
  ]
};

// Component usage
function Card() {
  return (
    <div className="bg-white rounded-lg shadow-lg p-6 hover:shadow-xl transition-shadow">
      <h2 className="text-2xl font-bold text-gray-900 mb-4">Card Title</h2>
      <p className="text-gray-600 leading-relaxed">Card content</p>
      <button className="mt-4 px-6 py-2 bg-brand-500 text-white rounded-md 
                         hover:bg-brand-600 focus:ring-2 focus:ring-brand-500 
                         focus:ring-offset-2 disabled:opacity-50">
        Action
      </button>
    </div>
  );
}

Example: Responsive design with Tailwind

// Mobile-first responsive layout
function ResponsiveGrid() {
  return (
    <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
      <div className="bg-gray-100 p-4 rounded-lg">Item 1</div>
      <div className="bg-gray-100 p-4 rounded-lg">Item 2</div>
      <div className="bg-gray-100 p-4 rounded-lg">Item 3</div>
    </div>
  );
}

// Dark mode support
function DarkModeButton() {
  return (
    <button className="bg-white dark:bg-gray-800 text-gray-900 dark:text-white 
                       border border-gray-300 dark:border-gray-700 px-4 py-2 rounded-md">
      Toggle
    </button>
  );
}
Tailwind Best Practices: Use @apply for repeated patterns, configure PurgeCSS for production builds (~3KB final CSS), use JIT mode for instant compilation, leverage plugins ecosystem.

13.3 CSS Variables Custom Properties

Native CSS custom properties for dynamic theming, runtime updates, and component-scoped styling without JavaScript overhead.

Feature Syntax Scope Use Case
Definition --variable-name: value; :root, element, inline Define reusable values
Usage var(--variable-name, fallback) Any CSS property Apply variable with fallback
Inheritance Cascades to descendants DOM tree Component theming
JavaScript Access getComputedStyle(), setProperty() Runtime Dynamic theme switching
Media Queries Change values per breakpoint Responsive Adaptive spacing/sizing

Example: Design system with CSS variables

/* styles/variables.css */
:root {
  /* Colors */
  --color-primary: #007acc;
  --color-secondary: #6c757d;
  --color-success: #28a745;
  --color-danger: #dc3545;
  --color-warning: #ffc107;
  
  /* Spacing scale */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  
  /* Typography */
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'Fira Code', monospace;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.25rem;
  
  /* Shadows */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
  
  /* Border radius */
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-lg: 1rem;
  
  /* Transitions */
  --transition-fast: 150ms ease;
  --transition-base: 250ms ease;
}

/* Component styles */
.button {
  padding: var(--space-md) var(--space-lg);
  background: var(--color-primary);
  color: white;
  border: none;
  border-radius: var(--radius-md);
  font-size: var(--font-size-base);
  transition: all var(--transition-base);
  box-shadow: var(--shadow-sm);
}

.button:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.card {
  padding: var(--space-lg);
  background: white;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}

Example: Runtime theme switching with JavaScript

// themes.ts
export const lightTheme = {
  '--color-bg': '#ffffff',
  '--color-text': '#1a1a1a',
  '--color-border': '#e5e5e5'
};

export const darkTheme = {
  '--color-bg': '#1a1a1a',
  '--color-text': '#ffffff',
  '--color-border': '#333333'
};

// ThemeProvider.tsx
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  useEffect(() => {
    const root = document.documentElement;
    const themeVars = theme === 'light' ? lightTheme : darkTheme;
    
    Object.entries(themeVars).forEach(([key, value]) => {
      root.style.setProperty(key, value);
    });
  }, [theme]);
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// Component usage
function ThemedCard() {
  return (
    <div style={{
      background: 'var(--color-bg)',
      color: 'var(--color-text)',
      border: '1px solid var(--color-border)'
    }}>
      Themed content
    </div>
  );
}
Browser Support: Excellent - All modern browsers support CSS custom properties. Use PostCSS plugins for older browser fallbacks.

13.4 Design Tokens Figma Integration

Design tokens are platform-agnostic design decisions (colors, spacing, typography) synced between design tools and code.

Tool/Format Purpose Integration Output
Figma Tokens Plugin Extract tokens from Figma designs JSON export, GitHub sync tokens.json with color, spacing, typography
Style Dictionary Transform tokens to platform formats Build system integration CSS, SCSS, JS, iOS, Android
Tokens Studio Manage multi-brand token sets Figma plugin with Git sync Semantic + alias tokens
Theo (Salesforce) Design token transformation CLI, Node API Multiple formats from single source

Example: Design tokens structure (tokens.json)

{
  "color": {
    "brand": {
      "primary": { "value": "#007acc", "type": "color" },
      "secondary": { "value": "#6c757d", "type": "color" }
    },
    "semantic": {
      "success": { "value": "{color.brand.primary}", "type": "color" },
      "danger": { "value": "#dc3545", "type": "color" }
    }
  },
  "spacing": {
    "xs": { "value": "4px", "type": "spacing" },
    "sm": { "value": "8px", "type": "spacing" },
    "md": { "value": "16px", "type": "spacing" },
    "lg": { "value": "24px", "type": "spacing" },
    "xl": { "value": "32px", "type": "spacing" }
  },
  "typography": {
    "fontFamily": {
      "sans": { "value": "Inter, system-ui, sans-serif", "type": "fontFamily" },
      "mono": { "value": "Fira Code, monospace", "type": "fontFamily" }
    },
    "fontSize": {
      "sm": { "value": "14px", "type": "fontSize" },
      "base": { "value": "16px", "type": "fontSize" },
      "lg": { "value": "20px", "type": "fontSize" },
      "xl": { "value": "24px", "type": "fontSize" }
    },
    "fontWeight": {
      "regular": { "value": "400", "type": "fontWeight" },
      "medium": { "value": "500", "type": "fontWeight" },
      "bold": { "value": "700", "type": "fontWeight" }
    }
  },
  "borderRadius": {
    "sm": { "value": "4px", "type": "borderRadius" },
    "md": { "value": "8px", "type": "borderRadius" },
    "lg": { "value": "16px", "type": "borderRadius" },
    "full": { "value": "9999px", "type": "borderRadius" }
  }
}

Example: Style Dictionary configuration and build

// style-dictionary.config.js
module.exports = {
  source: ['tokens/**/*.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      buildPath: 'src/styles/',
      files: [{
        destination: 'variables.css',
        format: 'css/variables'
      }]
    },
    js: {
      transformGroup: 'js',
      buildPath: 'src/tokens/',
      files: [{
        destination: 'tokens.js',
        format: 'javascript/es6'
      }]
    },
    typescript: {
      transformGroup: 'js',
      buildPath: 'src/tokens/',
      files: [{
        destination: 'tokens.ts',
        format: 'typescript/es6-declarations'
      }]
    }
  }
};

// Generated CSS output (variables.css)
:root {
  --color-brand-primary: #007acc;
  --color-brand-secondary: #6c757d;
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --font-family-sans: Inter, system-ui, sans-serif;
  --font-size-base: 16px;
  --font-weight-bold: 700;
  --border-radius-md: 8px;
}

// Generated TypeScript output (tokens.ts)
export const tokens = {
  color: {
    brand: {
      primary: '#007acc',
      secondary: '#6c757d'
    }
  },
  spacing: {
    xs: '4px',
    sm: '8px',
    md: '16px'
  }
};
Workflow: Design in Figma → Export tokens via plugin → Transform with Style Dictionary → Generate CSS/JS/iOS/Android → Import in codebase. Automate with CI/CD for design-dev sync.

13.5 Dark Mode Light Mode Toggle

Implement theme switching with system preference detection, persistent storage, and smooth transitions.

Approach Implementation Pros Cons
CSS Media Query @media (prefers-color-scheme: dark) Native, automatic, no JS No manual toggle
Data Attribute [data-theme="dark"] on root Easy CSS targeting, semantic Requires JS for toggle
Class Toggle .dark class on root Simple, Tailwind compatible Flash of wrong theme (FOUT)
CSS Variables Change variable values dynamically Smooth transitions, runtime More complex setup

Example: Complete dark mode implementation with persistence

// theme.css
:root {
  --color-bg: #ffffff;
  --color-text: #1a1a1a;
  --color-border: #e5e5e5;
  --color-card-bg: #f8f9fa;
}

[data-theme="dark"] {
  --color-bg: #1a1a1a;
  --color-text: #ffffff;
  --color-border: #333333;
  --color-card-bg: #2d2d2d;
}

body {
  background: var(--color-bg);
  color: var(--color-text);
  transition: background-color 0.3s ease, color 0.3s ease;
}

// useTheme.ts hook
import { useEffect, useState } from 'react';

type Theme = 'light' | 'dark' | 'system';

export function useTheme() {
  const [theme, setTheme] = useState<Theme>(() => {
    const stored = localStorage.getItem('theme') as Theme;
    return stored || 'system';
  });

  useEffect(() => {
    const root = document.documentElement;
    const systemPreference = window.matchMedia('(prefers-color-scheme: dark)');
    
    const applyTheme = (newTheme: Theme) => {
      let appliedTheme: 'light' | 'dark';
      
      if (newTheme === 'system') {
        appliedTheme = systemPreference.matches ? 'dark' : 'light';
      } else {
        appliedTheme = newTheme;
      }
      
      root.setAttribute('data-theme', appliedTheme);
      localStorage.setItem('theme', newTheme);
    };
    
    applyTheme(theme);
    
    // Listen for system preference changes
    const handleChange = (e: MediaQueryListEvent) => {
      if (theme === 'system') {
        root.setAttribute('data-theme', e.matches ? 'dark' : 'light');
      }
    };
    
    systemPreference.addEventListener('change', handleChange);
    return () => systemPreference.removeEventListener('change', handleChange);
  }, [theme]);
  
  return { theme, setTheme };
}

// ThemeToggle.tsx component
function ThemeToggle() {
  const { theme, setTheme } = useTheme();
  
  return (
    <div className="theme-toggle">
      <button 
        onClick={() => setTheme('light')}
        aria-pressed={theme === 'light'}
      >
        ☀️ Light
      </button>
      <button 
        onClick={() => setTheme('dark')}
        aria-pressed={theme === 'dark'}
      >
        🌙 Dark
      </button>
      <button 
        onClick={() => setTheme('system')}
        aria-pressed={theme === 'system'}
      >
        💻 System
      </button>
    </div>
  );
}

Example: Prevent flash of wrong theme (critical inline script)

<!-- Place in <head> before any CSS -->
<script>
  (function() {
    const theme = localStorage.getItem('theme') || 'system';
    const systemPreference = window.matchMedia('(prefers-color-scheme: dark)').matches;
    
    let appliedTheme;
    if (theme === 'system') {
      appliedTheme = systemPreference ? 'dark' : 'light';
    } else {
      appliedTheme = theme;
    }
    
    document.documentElement.setAttribute('data-theme', appliedTheme);
  })();
</script>

<!-- Next.js implementation in _document.tsx -->
import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html>
      <Head />
      <body>
        <script dangerouslySetInnerHTML={{
          __html: `
            (function() {
              const theme = localStorage.getItem('theme') || 'system';
              const dark = theme === 'dark' || 
                (theme === 'system' && 
                 window.matchMedia('(prefers-color-scheme: dark)').matches);
              document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light');
            })();
          `
        }} />
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}
Performance: Avoid transition: all on theme toggle (causes layout thrashing). Target specific properties: background-color, color, border-color. Use prefers-reduced-motion for accessibility.

13.6 Component Library MUI Chakra UI

Production-ready component libraries with theming, accessibility, and extensive component catalogs.

Library Key Features Best For Bundle Size
Material-UI (MUI) Material Design, comprehensive components, theming system Enterprise apps, admin panels ~80KB core + components
Chakra UI Accessible, composable, dark mode, styled-system Modern apps, fast development ~45KB + components
Ant Design Enterprise UI, rich components, internationalization Data-heavy dashboards ~60KB core + components
Mantine Full-featured, hooks library, 120+ components Complete solution, TypeScript ~50KB + components
Radix UI Unstyled, accessible primitives, headless Custom designs, full control Minimal, per-component
shadcn/ui Copy-paste components, Radix + Tailwind Full customization, no dependencies Only what you use

Example: Material-UI (MUI) with custom theme

// theme.ts
import { createTheme } from '@mui/material/styles';

export const theme = createTheme({
  palette: {
    primary: {
      main: '#007acc',
      light: '#42a5f5',
      dark: '#005fa3'
    },
    secondary: {
      main: '#6c757d'
    },
    mode: 'light' // or 'dark'
  },
  typography: {
    fontFamily: 'Inter, system-ui, sans-serif',
    h1: {
      fontSize: '2.5rem',
      fontWeight: 700
    }
  },
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          borderRadius: 8,
          textTransform: 'none',
          boxShadow: 'none'
        }
      },
      defaultProps: {
        disableElevation: true
      }
    }
  }
});

// App.tsx
import { ThemeProvider } from '@mui/material/styles';
import { Button, TextField, Card, CardContent } from '@mui/material';

function App() {
  return (
    <ThemeProvider theme={theme}>
      <Card>
        <CardContent>
          <TextField 
            label="Email" 
            variant="outlined" 
            fullWidth 
            margin="normal"
          />
          <Button variant="contained" color="primary">
            Submit
          </Button>
        </CardContent>
      </Card>
    </ThemeProvider>
  );
}

Example: Chakra UI with composition and dark mode

// App.tsx
import { ChakraProvider, extendTheme, Box, Button, Input, Stack } from '@chakra-ui/react';

const theme = extendTheme({
  colors: {
    brand: {
      50: '#e3f2fd',
      500: '#007acc',
      900: '#003d66'
    }
  },
  config: {
    initialColorMode: 'light',
    useSystemColorMode: true
  }
});

function App() {
  return (
    <ChakraProvider theme={theme}>
      <Box p={8} bg="gray.50" minH="100vh">
        <Stack spacing={4} maxW="md">
          <Input placeholder="Email" size="lg" />
          <Button 
            colorScheme="brand" 
            size="lg" 
            _hover={{ transform: 'translateY(-2px)', boxShadow: 'lg' }}
          >
            Submit
          </Button>
        </Stack>
      </Box>
    </ChakraProvider>
  );
}

// ColorModeToggle.tsx
import { useColorMode, IconButton } from '@chakra-ui/react';
import { SunIcon, MoonIcon } from '@chakra-ui/icons';

function ColorModeToggle() {
  const { colorMode, toggleColorMode } = useColorMode();
  return (
    <IconButton
      aria-label="Toggle color mode"
      icon={colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
      onClick={toggleColorMode}
    />
  );
}

Example: shadcn/ui with Radix primitives (copy-paste approach)

// components/ui/button.tsx (copied from shadcn/ui)
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline: "border border-input hover:bg-accent hover:text-accent-foreground",
        ghost: "hover:bg-accent hover:text-accent-foreground"
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-9 rounded-md px-3",
        lg: "h-11 rounded-md px-8"
      }
    },
    defaultVariants: {
      variant: "default",
      size: "default"
    }
  }
);

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    const Comp = asChild ? Slot : "button";
    return (
      <Comp
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    );
  }
);

// Usage
import { Button } from "@/components/ui/button";

function MyComponent() {
  return (
    <>
      <Button variant="default">Default</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="ghost" size="sm">Small Ghost</Button>
    </>
  );
}
MUI Pros: Material Design consistency, massive component library (100+), strong TypeScript support, excellent documentation, MUI X for advanced components (DataGrid, Date Pickers).
Chakra UI Pros: Excellent DX with style props, built-in dark mode, smaller bundle, composable design, fast setup. Great for startups and modern apps.

Styling Strategy Decision Matrix

Use Case Recommended Approach Rationale
Rapid prototyping Tailwind CSS + shadcn/ui Fast development, no custom CSS, full control
Enterprise dashboard MUI or Ant Design Rich components, data tables, consistency
Design system from scratch Radix UI + CSS Variables + Tokens Full customization, brand consistency
Performance-critical Tailwind CSS + Linaria Zero-runtime CSS, optimal bundle size
Dynamic theming CSS Variables + Chakra UI Runtime theme switching, easy dark mode

14. Internationalization Implementation Stack

14.1 React-intl i18next Setup Configuration

Comprehensive i18n solutions for React apps with translation management, formatting, and pluralization support.

Library Core Features Best For Bundle Size
react-intl (FormatJS) ICU message format, date/number formatting, React hooks Enterprise apps, complex formatting ~45KB
react-i18next Plugin ecosystem, lazy loading, backend integration Large projects, dynamic translations ~30KB + i18next core
next-intl Next.js optimized, SSR/SSG support, type-safe Next.js apps ~15KB
LinguiJS CLI extraction, compile-time optimization, minimal runtime Performance-critical apps ~5KB runtime

Example: react-i18next setup with TypeScript

// i18n.ts - Configuration
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';

i18n
  .use(Backend) // Load translations from backend
  .use(LanguageDetector) // Detect user language
  .use(initReactI18next) // Pass i18n to react-i18next
  .init({
    fallbackLng: 'en',
    debug: process.env.NODE_ENV === 'development',
    interpolation: {
      escapeValue: false // React already escapes
    },
    backend: {
      loadPath: '/locales/{{lng}}/{{ns}}.json'
    },
    ns: ['common', 'auth', 'dashboard'],
    defaultNS: 'common',
    react: {
      useSuspense: true
    }
  });

export default i18n;

// locales/en/common.json
{
  "welcome": "Welcome, {{name}}!",
  "itemCount": "You have {{count}} item",
  "itemCount_plural": "You have {{count}} items",
  "updated": "Last updated: {{date, datetime}}"
}

// locales/es/common.json
{
  "welcome": "¡Bienvenido, {{name}}!",
  "itemCount": "Tienes {{count}} artículo",
  "itemCount_plural": "Tienes {{count}} artículos",
  "updated": "Última actualización: {{date, datetime}}"
}

// App.tsx
import './i18n';
import { Suspense } from 'react';

function App() {
  return (
    <Suspense fallback="Loading...">
      <MainApp />
    </Suspense>
  );
}

// Component usage
import { useTranslation } from 'react-i18next';

function Dashboard() {
  const { t, i18n } = useTranslation('common');
  
  return (
    <div>
      <h1>{t('welcome', { name: 'John' })}</h1>
      <p>{t('itemCount', { count: 5 })}</p>
      <p>{t('updated', { date: new Date() })}</p>
      
      <button onClick={() => i18n.changeLanguage('es')}>
        Español
      </button>
      <button onClick={() => i18n.changeLanguage('en')}>
        English
      </button>
    </div>
  );
}

Example: react-intl (FormatJS) implementation

// App.tsx
import { IntlProvider } from 'react-intl';
import { useState } from 'react';
import enMessages from './locales/en.json';
import esMessages from './locales/es.json';

const messages = {
  en: enMessages,
  es: esMessages
};

function App() {
  const [locale, setLocale] = useState('en');
  
  return (
    <IntlProvider 
      messages={messages[locale]} 
      locale={locale}
      defaultLocale="en"
    >
      <Dashboard onLocaleChange={setLocale} />
    </IntlProvider>
  );
}

// Component with react-intl hooks
import { 
  useIntl, 
  FormattedMessage, 
  FormattedNumber, 
  FormattedDate 
} from 'react-intl';

function Dashboard({ onLocaleChange }) {
  const intl = useIntl();
  
  return (
    <div>
      <h1>
        <FormattedMessage 
          id="welcome"
          defaultMessage="Welcome, {name}!"
          values={{ name: 'John' }}
        />
      </h1>
      
      <p>
        <FormattedNumber 
          value={1234.56} 
          style="currency" 
          currency="USD" 
        />
      </p>
      
      <p>
        <FormattedDate 
          value={new Date()} 
          year="numeric"
          month="long"
          day="numeric"
        />
      </p>
      
      {/* Imperative usage */}
      <input 
        placeholder={intl.formatMessage({ 
          id: 'search.placeholder',
          defaultMessage: 'Search...'
        })}
      />
    </div>
  );
}
Comparison: Use react-i18next for flexibility and ecosystem. Use react-intl for standardized ICU formatting. Use LinguiJS for smallest bundle and compile-time extraction.

14.2 Dynamic Locale Loading Lazy i18n

Load translation files on-demand to reduce initial bundle size and improve performance for multi-language apps.

Strategy Implementation Benefits Trade-offs
Code Splitting Dynamic import() per locale Smaller initial bundle Network request on language change
Namespace Splitting Split by feature/page Load only needed translations More HTTP requests
Backend Loading Fetch from API/CDN No rebuild for translation updates Runtime dependency
Preloading Prefetch likely locales Instant switching Additional bandwidth usage

Example: Dynamic locale loading with react-i18next

// i18n.ts - Lazy loading configuration
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

// Custom backend for dynamic imports
const loadResources = async (lng: string, ns: string) => {
  try {
    const resources = await import(`./locales/${lng}/${ns}.json`);
    return resources.default;
  } catch (error) {
    console.error(`Failed to load ${lng}/${ns}`, error);
    return {};
  }
};

i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .use({
    type: 'backend',
    read(language, namespace, callback) {
      loadResources(language, namespace)
        .then(resources => callback(null, resources))
        .catch(error => callback(error, null));
    }
  })
  .init({
    fallbackLng: 'en',
    ns: ['common', 'auth', 'dashboard', 'settings'],
    defaultNS: 'common',
    react: {
      useSuspense: true
    },
    // Preload common namespaces
    preload: ['en'],
    load: 'languageOnly' // 'en-US' -> 'en'
  });

export default i18n;

// Hook for namespace loading
import { useTranslation } from 'react-i18next';
import { useEffect } from 'react';

function useLazyTranslation(namespace: string) {
  const { t, i18n, ready } = useTranslation(namespace, { useSuspense: false });
  
  useEffect(() => {
    if (!i18n.hasResourceBundle(i18n.language, namespace)) {
      i18n.loadNamespaces(namespace);
    }
  }, [i18n, namespace]);
  
  return { t, ready };
}

// Component usage
function SettingsPage() {
  const { t, ready } = useLazyTranslation('settings');
  
  if (!ready) return <div>Loading translations...</div>
  
  return (
    <div>
      <h1>{t('title')}</h1>
      <p>{t('description')}</p>
    </div>
  );
}

Example: Webpack/Vite magic comments for chunk naming

// loadLocale.ts
export async function loadLocale(locale: string) {
  // Webpack magic comments for chunk naming
  const messages = await import(
    /* webpackChunkName: "locale-[request]" */
    /* webpackMode: "lazy" */
    `./locales/${locale}/messages.json`
  );
  
  return messages.default;
}

// Vite dynamic import
export async function loadLocaleVite(locale: string) {
  const modules = import.meta.glob('./locales/*/messages.json');
  const path = `./locales/${locale}/messages.json`;
  
  if (modules[path]) {
    const messages = await modules[path]();
    return messages.default;
  }
  
  throw new Error(`Locale ${locale} not found`);
}

// App.tsx - Progressive enhancement
import { Suspense, lazy } from 'react';

const LocaleProvider = lazy(() => 
  import(/* webpackChunkName: "i18n-provider" */ './LocaleProvider')
);

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <LocaleProvider>
        <Routes />
      </LocaleProvider>
    </Suspense>
  );
}

Example: Preloading strategy for better UX

// useLocalePreload.ts
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';

const COMMON_LOCALES = ['en', 'es', 'fr', 'de'];

export function useLocalePreload() {
  const { i18n } = useTranslation();
  
  useEffect(() => {
    // Preload on idle
    if ('requestIdleCallback' in window) {
      requestIdleCallback(() => {
        COMMON_LOCALES.forEach(locale => {
          if (locale !== i18n.language) {
            // Prefetch but don't block
            import(`./locales/${locale}/common.json`).catch(() => {});
          }
        });
      });
    }
  }, [i18n.language]);
}

// Link preload in HTML head
function injectPreloadLinks(locales: string[]) {
  locales.forEach(locale => {
    const link = document.createElement('link');
    link.rel = 'prefetch';
    link.as = 'fetch';
    link.href = `/locales/${locale}/common.json`;
    link.crossOrigin = 'anonymous';
    document.head.appendChild(link);
  });
}

// Next.js implementation
import Head from 'next/head';

function LocalePreload({ locales }: { locales: string[] }) {
  return (
    <Head>
      {locales.map(locale => (
        <link
          key={locale}
          rel="prefetch"
          as="fetch"
          href={`/locales/${locale}/common.json`}
          crossOrigin="anonymous"
        />
      ))}
    </Head>
  );
}
Best Practice: Load common namespace eagerly, lazy-load feature-specific namespaces. Preload user's preferred alternate languages during idle time. Cache translations in localStorage.

14.3 Pluralization ICU Message Format

Handle complex pluralization rules across different languages using ICU MessageFormat standard.

Feature Syntax Use Case Example
Simple Plural {count, plural, one{#} other{#}} Item counts 1 item / 5 items
Select {gender, select, male{} female{}} Gender-based text He/She variations
SelectOrdinal {num, selectordinal, one{#st}} Ordinal numbers 1st, 2nd, 3rd
Nested Combine plural + select Complex messages Gender + count variations

Example: ICU MessageFormat pluralization rules

// English pluralization (2 forms)
{
  "itemCount": "{count, plural, one {# item} other {# items}}"
}

// Usage: 0 items, 1 item, 2 items, 100 items

// Polish pluralization (3 forms)
{
  "itemCount": "{count, plural, one {# przedmiot} few {# przedmioty} many {# przedmiotów} other {# przedmiotu}}"
}

// Arabic pluralization (6 forms!)
{
  "itemCount": "{count, plural, zero {لا عناصر} one {عنصر واحد} two {عنصران} few {# عناصر} many {# عنصرًا} other {# عنصر}}"
}

// Complex example with select + plural
{
  "taskStatus": "{taskCount, plural, =0 {No tasks} one {# task} other {# tasks}} {status, select, pending {pending} completed {completed} failed {failed} other {unknown}}"
}

// Nested gender + plural
{
  "friendRequest": "{gender, select, male {He has} female {She has} other {They have}} {count, plural, one {# friend request} other {# friend requests}}"
}

Example: react-intl with ICU MessageFormat

import { FormattedMessage, useIntl } from 'react-intl';

// Translation file (en.json)
{
  "cart.items": "You have {itemCount, plural, =0 {no items} one {# item} other {# items}} in your cart",
  "user.greeting": "{name} {gender, select, male {is online. Say hi to him!} female {is online. Say hi to her!} other {is online. Say hi!}}",
  "finish.position": "You finished in {position, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} place"
}

// Component usage
function ShoppingCart({ itemCount }: { itemCount: number }) {
  return (
    <div>
      <FormattedMessage 
        id="cart.items"
        values={{ itemCount }}
      />
    </div>
  );
}

function UserStatus({ name, gender }: { name: string; gender: 'male' | 'female' | 'other' }) {
  return (
    <FormattedMessage 
      id="user.greeting"
      values={{ name, gender }}
    />
  );
}

function RaceResult({ position }: { position: number }) {
  const intl = useIntl();
  
  return (
    <div>
      {intl.formatMessage(
        { id: 'finish.position' },
        { position }
      )}
    </div>
  );
}

Example: i18next with ICU plugin

// i18n.ts - Enable ICU format
import i18n from 'i18next';
import ICU from 'i18next-icu';
import { initReactI18next } from 'react-i18next';

i18n
  .use(ICU) // Add ICU plugin
  .use(initReactI18next)
  .init({
    fallbackLng: 'en',
    resources: {
      en: {
        translation: {
          "notifications": "You have {count, plural, =0 {no notifications} one {# notification} other {# notifications}}",
          "fileSize": "{size, number} {unit, select, KB {kilobytes} MB {megabytes} GB {gigabytes} other {bytes}}"
        }
      }
    }
  });

// Component usage
import { useTranslation } from 'react-i18next';

function Notifications({ count }: { count: number }) {
  const { t } = useTranslation();
  
  return <div>{t('notifications', { count })}</div>;
}

function FileInfo({ size, unit }: { size: number; unit: string }) {
  const { t } = useTranslation();
  
  return <div>{t('fileSize', { size, unit })}</div>;
}
Performance Note: ICU MessageFormat parsing has runtime cost. Use compile-time extraction (LinguiJS) for production apps or cache parsed messages. Simple plural/other is sufficient for many use cases.

14.4 RTL LTR CSS Logical Properties

Support right-to-left (RTL) languages like Arabic and Hebrew with CSS logical properties for automatic layout mirroring.

Physical Property Logical Property LTR Value RTL Value
margin-left margin-inline-start Left margin Right margin
margin-right margin-inline-end Right margin Left margin
padding-left padding-inline-start Left padding Right padding
border-left border-inline-start Left border Right border
text-align: left text-align: start Align left Align right
float: left float: inline-start Float left Float right

Example: CSS logical properties for RTL support

/* Traditional approach (manual RTL) */
.card {
  margin-left: 16px;
  padding-right: 24px;
  border-left: 2px solid blue;
}

[dir="rtl"] .card {
  margin-left: 0;
  margin-right: 16px;
  padding-right: 0;
  padding-left: 24px;
  border-left: none;
  border-right: 2px solid blue;
}

/* Modern approach (automatic RTL) */
.card {
  margin-inline-start: 16px;
  padding-inline-end: 24px;
  border-inline-start: 2px solid blue;
}
/* No RTL override needed! */

/* Complete example */
.sidebar {
  /* Inline = horizontal (left/right) */
  padding-inline-start: 20px;
  padding-inline-end: 10px;
  margin-inline: 8px; /* shorthand for start + end */
  
  /* Block = vertical (top/bottom) - no change in RTL */
  padding-block-start: 16px;
  padding-block-end: 16px;
  margin-block: 12px;
  
  /* Positioning */
  inset-inline-start: 0; /* left in LTR, right in RTL */
  
  /* Text alignment */
  text-align: start; /* left in LTR, right in RTL */
}

.icon {
  margin-inline-end: 8px; /* Space after icon */
}

.arrow {
  /* Transform for RTL */
  transform: scaleX(1);
}

[dir="rtl"] .arrow {
  transform: scaleX(-1); /* Flip horizontally */
}

Example: React RTL implementation with context

// DirectionProvider.tsx
import { createContext, useContext, useEffect } from 'react';

type Direction = 'ltr' | 'rtl';

const DirectionContext = createContext<Direction>('ltr');

export function DirectionProvider({ 
  children, 
  direction 
}: { 
  children: React.ReactNode; 
  direction: Direction 
}) {
  useEffect(() => {
    document.documentElement.setAttribute('dir', direction);
    document.documentElement.setAttribute('lang', direction === 'rtl' ? 'ar' : 'en');
  }, [direction]);
  
  return (
    <DirectionContext.Provider value={direction}>
      {children}
    </DirectionContext.Provider>
  );
}

export const useDirection = () => useContext(DirectionContext);

// App.tsx
import { useTranslation } from 'react-i18next';

const RTL_LANGUAGES = ['ar', 'he', 'fa', 'ur'];

function App() {
  const { i18n } = useTranslation();
  const direction = RTL_LANGUAGES.includes(i18n.language) ? 'rtl' : 'ltr';
  
  return (
    <DirectionProvider direction={direction}>
      <MainApp />
    </DirectionProvider>
  );
}

// Component with direction-aware styles
import styled from 'styled-components';

const Card = styled.div`
  padding-inline-start: 20px;
  padding-inline-end: 10px;
  border-inline-start: 3px solid var(--primary-color);
  
  .icon {
    margin-inline-end: 8px;
  }
`;

function MyCard() {
  const direction = useDirection();
  
  return (
    <Card>
      <span className="icon">→</span>
      {direction === 'rtl' ? 'النص العربي' : 'English text'}
    </Card>
  );
}

Example: Tailwind CSS with RTL plugin

// tailwind.config.js
module.exports = {
  plugins: [require('tailwindcss-rtl')],
};

// Component with RTL-aware utilities
function Navbar() {
  return (
    <nav className="flex items-center">
      <img 
        src="/logo.png" 
        className="ms-4 me-2" // ms = margin-inline-start, me = margin-inline-end
        alt="Logo" 
      />
      <ul className="flex gap-4">
        <li className="ps-4">Home</li> {/* ps = padding-inline-start */}
        <li className="ps-4">About</li>
      </ul>
      <button className="ms-auto">Login</button> {/* Push to end */}
    </nav>
  );
}

// Custom RTL utilities
<div className="ltr:text-left rtl:text-right">
  Directional text
</div>
Browser Support: Excellent - All modern browsers support CSS logical properties. Use dir="rtl" on HTML element. Test with Arabic/Hebrew content thoroughly.

14.5 Date-fns Timezone Locale Formatting

Format dates, times, and numbers according to user's locale and timezone with proper internationalization support.

Library Features Use Case Bundle Size
date-fns Modular, tree-shakeable, 80+ locales, immutable Modern apps, small bundles ~2KB per function
date-fns-tz Timezone support addon for date-fns Multi-timezone apps ~10KB + date-fns
Luxon Modern API, Intl wrapper, timezone native Complex date logic ~70KB
Day.js Moment.js alternative, plugins, small Simple date needs ~7KB
Intl API (Native) Browser built-in, no dependencies Basic formatting 0KB

Example: date-fns with locale formatting

import { format, formatDistance, formatRelative } from 'date-fns';
import { enUS, es, ar, ja, de } from 'date-fns/locale';

// Locale mapping
const locales = { en: enUS, es, ar, ja, de };

function formatDate(date: Date, locale: string) {
  return format(date, 'PPpp', { locale: locales[locale] });
}

// Usage examples
const date = new Date(2025, 0, 15, 14, 30);

// English: "Jan 15, 2025, 2:30 PM"
format(date, 'PPpp', { locale: enUS });

// Spanish: "15 ene 2025, 14:30"
format(date, 'PPpp', { locale: es });

// Arabic: "١٥ يناير ٢٠٢٥، ١٤:٣٠"
format(date, 'PPpp', { locale: ar });

// Relative time
formatDistance(date, new Date(), { 
  addSuffix: true, 
  locale: es 
}); // "hace 3 días"

// Custom formats
format(date, 'EEEE, MMMM do yyyy', { locale: de });
// "Mittwoch, Januar 15. 2025"

// React hook for localized dates
import { useTranslation } from 'react-i18next';

function useLocalizedDate() {
  const { i18n } = useTranslation();
  const locale = locales[i18n.language] || enUS;
  
  const formatLocalizedDate = (date: Date, formatStr: string) => {
    return format(date, formatStr, { locale });
  };
  
  const formatRelativeTime = (date: Date) => {
    return formatDistance(date, new Date(), { 
      addSuffix: true, 
      locale 
    });
  };
  
  return { formatLocalizedDate, formatRelativeTime };
}

Example: Timezone handling with date-fns-tz

import { format } from 'date-fns';
import { formatInTimeZone, toZonedTime, fromZonedTime } from 'date-fns-tz';
import { enUS } from 'date-fns/locale';

// Display date in user's timezone
function formatUserTimezone(date: Date, userTimezone: string) {
  return formatInTimeZone(
    date, 
    userTimezone, 
    'yyyy-MM-dd HH:mm:ss zzz',
    { locale: enUS }
  );
}

// Examples
const utcDate = new Date('2025-01-15T14:30:00Z');

formatUserTimezone(utcDate, 'America/New_York');
// "2025-01-15 09:30:00 EST"

formatUserTimezone(utcDate, 'Asia/Tokyo');
// "2025-01-15 23:30:00 JST"

formatUserTimezone(utcDate, 'Europe/London');
// "2025-01-15 14:30:00 GMT"

// Convert between timezones
const tokyoTime = toZonedTime(utcDate, 'Asia/Tokyo');
const nyTime = fromZonedTime(tokyoTime, 'America/New_York');

// React component with timezone display
function EventTime({ eventDate, timezone }: { 
  eventDate: Date; 
  timezone: string;
}) {
  const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
  
  return (
    <div>
      <p>Event time: {formatInTimeZone(eventDate, timezone, 'PPpp')}</p>
      <p>Your time: {formatInTimeZone(eventDate, userTimezone, 'PPpp')}</p>
    </div>
  );
}

Example: Native Intl API for formatting

// Date formatting with Intl.DateTimeFormat
const date = new Date('2025-01-15T14:30:00');

// English (US)
new Intl.DateTimeFormat('en-US', {
  dateStyle: 'full',
  timeStyle: 'short'
}).format(date);
// "Wednesday, January 15, 2025 at 2:30 PM"

// Spanish (Spain)
new Intl.DateTimeFormat('es-ES', {
  dateStyle: 'full',
  timeStyle: 'short'
}).format(date);
// "miércoles, 15 de enero de 2025, 14:30"

// Number formatting with Intl.NumberFormat
const number = 1234567.89;

// Currency
new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).format(number);
// "$1,234,567.89"

new Intl.NumberFormat('ja-JP', {
  style: 'currency',
  currency: 'JPY'
}).format(number);
// "¥1,234,568"

// Percentage
new Intl.NumberFormat('en-US', {
  style: 'percent',
  minimumFractionDigits: 2
}).format(0.1234);
// "12.34%"

// Relative time with Intl.RelativeTimeFormat
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
rtf.format(-1, 'day'); // "yesterday"
rtf.format(2, 'week'); // "in 2 weeks"

const rtfEs = new Intl.RelativeTimeFormat('es', { numeric: 'auto' });
rtfEs.format(-1, 'day'); // "ayer"
rtfEs.format(2, 'week'); // "dentro de 2 semanas"

// React hook for Intl formatting
function useIntlFormatting(locale: string) {
  const dateFormatter = new Intl.DateTimeFormat(locale, {
    dateStyle: 'medium',
    timeStyle: 'short'
  });
  
  const currencyFormatter = new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: 'USD'
  });
  
  const relativeTimeFormatter = new Intl.RelativeTimeFormat(locale, {
    numeric: 'auto'
  });
  
  return {
    formatDate: (date: Date) => dateFormatter.format(date),
    formatCurrency: (amount: number) => currencyFormatter.format(amount),
    formatRelativeTime: (value: number, unit: Intl.RelativeTimeFormatUnit) => 
      relativeTimeFormatter.format(value, unit)
  };
}
Timezone Pitfall: Always store dates in UTC on backend. Convert to user's timezone only for display. Use Date.prototype.toISOString() for API communication. Avoid new Date(string) parsing across timezones.

14.6 Translation Keys TypeScript Validation

Enforce type safety for translation keys to catch missing translations at compile-time instead of runtime.

Approach Tool Benefits Setup Complexity
Type Generation i18next + typesafe-i18n Auto-complete, type errors for invalid keys Medium
CLI Extraction LinguiJS, react-intl CLI Extract keys from code, detect unused High
Const Assertion TypeScript as const Simple, no build step Low
Schema Validation Zod, Yup Runtime + compile validation Medium

Example: TypeScript with i18next typed translations

// locales/en/translation.json
{
  "common": {
    "welcome": "Welcome",
    "logout": "Logout"
  },
  "auth": {
    "login": {
      "title": "Sign In",
      "email": "Email Address",
      "password": "Password"
    }
  },
  "errors": {
    "required": "This field is required",
    "invalid_email": "Invalid email format"
  }
}

// i18next.d.ts - Type augmentation
import 'i18next';
import type translation from './locales/en/translation.json';

declare module 'i18next' {
  interface CustomTypeOptions {
    defaultNS: 'translation';
    resources: {
      translation: typeof translation;
    };
  }
}

// Now TypeScript knows all translation keys!
import { useTranslation } from 'react-i18next';

function MyComponent() {
  const { t } = useTranslation();
  
  // ✅ Valid - autocomplete works!
  t('common.welcome');
  t('auth.login.title');
  
  // ❌ TypeScript error - key doesn't exist
  t('common.invalid'); // Error: Property 'invalid' does not exist
  
  // ✅ Nested object access with type safety
  t('auth.login.email');
  
  return <div>{t('common.welcome')}</div>;
}

Example: Generate types from translation files

// scripts/generate-i18n-types.ts
import fs from 'fs';
import path from 'path';

type TranslationKeys<T, Prefix extends string = ''> = {
  [K in keyof T]: T[K] extends object
    ? TranslationKeys<T[K], `${Prefix}${K & string}.`>
    : `${Prefix}${K & string}`;
}[keyof T];

function generateTypes() {
  const enTranslations = JSON.parse(
    fs.readFileSync('./locales/en/translation.json', 'utf-8')
  );
  
  const types = `
// Auto-generated - do not edit
import type en from './locales/en/translation.json';

export type TranslationKey = TranslationKeys<typeof en>;

export type TranslationKeys<T, Prefix extends string = ''> = {
  [K in keyof T]: T[K] extends object
    ? TranslationKeys<T[K], \`\${Prefix}\${K & string}.\`>
    : \`\${Prefix}\${K & string}\`;
}[keyof T];
  `;
  
  fs.writeFileSync('./src/types/i18n.ts', types);
}

generateTypes();

// Usage with custom hook
import type { TranslationKey } from './types/i18n';
import { useTranslation as useI18next } from 'react-i18next';

export function useTranslation() {
  const { t, ...rest } = useI18next();
  
  const typedT = (key: TranslationKey, options?: any) => {
    return t(key, options);
  };
  
  return { t: typedT, ...rest };
}

Example: typesafe-i18n library (zero-dependency, full type safety)

// Installation: npm install typesafe-i18n

// locales/en.json
{
  "HI": "Hi {name:string}!",
  "ITEMS": "You have {count:number} {count:plural(item|items)}",
  "PRICE": "Price: {amount:number|currency(USD)}"
}

// Generated types (automatic)
type Translation = {
  HI: (params: { name: string }) => string;
  ITEMS: (params: { count: number }) => string;
  PRICE: (params: { amount: number }) => string;
}

// Usage
import { useI18nContext } from './i18nContext';

function MyComponent() {
  const { LL } = useI18nContext(); // LL = Localized Language
  
  // ✅ Type-safe with parameter validation
  LL.HI({ name: 'John' }); // "Hi John!"
  
  // ❌ TypeScript error - missing required parameter
  LL.HI({ }); // Error: Property 'name' is missing
  
  // ❌ TypeScript error - wrong type
  LL.HI({ name: 123 }); // Error: Type 'number' is not assignable to 'string'
  
  // ✅ Pluralization
  LL.ITEMS({ count: 1 }); // "You have 1 item"
  LL.ITEMS({ count: 5 }); // "You have 5 items"
  
  // ✅ Formatted values
  LL.PRICE({ amount: 99.99 }); // "Price: $99.99"
  
  return <div>{LL.HI({ name: 'World' })}</div>;
}

Example: Validation in CI/CD pipeline

// scripts/validate-translations.ts
import fs from 'fs';
import path from 'path';

interface ValidationResult {
  valid: boolean;
  errors: string[];
}

function getTranslationKeys(obj: any, prefix = ''): string[] {
  return Object.keys(obj).flatMap(key => {
    const value = obj[key];
    const fullKey = prefix ? `${prefix}.${key}` : key;
    
    if (typeof value === 'object' && value !== null) {
      return getTranslationKeys(value, fullKey);
    }
    return [fullKey];
  });
}

function validateTranslations(): ValidationResult {
  const errors: string[] = [];
  const localesDir = path.join(__dirname, '../locales');
  const locales = fs.readdirSync(localesDir);
  
  // Load base locale (English)
  const baseLocale = 'en';
  const baseTranslations = JSON.parse(
    fs.readFileSync(path.join(localesDir, baseLocale, 'translation.json'), 'utf-8')
  );
  const baseKeys = new Set(getTranslationKeys(baseTranslations));
  
  // Check each locale
  locales.forEach(locale => {
    if (locale === baseLocale) return;
    
    const translations = JSON.parse(
      fs.readFileSync(path.join(localesDir, locale, 'translation.json'), 'utf-8')
    );
    const keys = new Set(getTranslationKeys(translations));
    
    // Check for missing keys
    baseKeys.forEach(key => {
      if (!keys.has(key)) {
        errors.push(`[${locale}] Missing translation key: ${key}`);
      }
    });
    
    // Check for extra keys
    keys.forEach(key => {
      if (!baseKeys.has(key)) {
        errors.push(`[${locale}] Extra translation key: ${key}`);
      }
    });
  });
  
  return {
    valid: errors.length === 0,
    errors
  };
}

// Run validation
const result = validateTranslations();
if (!result.valid) {
  console.error('Translation validation failed:');
  result.errors.forEach(error => console.error(`  - ${error}`));
  process.exit(1);
}

console.log('✅ All translations are valid!');

// package.json script
{
  "scripts": {
    "validate:i18n": "ts-node scripts/validate-translations.ts",
    "precommit": "npm run validate:i18n && npm run type-check"
  }
}

I18n Implementation Checklist

15. Modern Project Structure Implementation

15.1 Monorepo Nx Lerna Workspace Setup

Manage multiple related packages in a single repository with shared dependencies, unified tooling, and efficient builds.

Tool Key Features Best For Learning Curve
Nx Smart rebuilds, computation caching, code generators, dependency graph Large teams, enterprise apps Medium-High
Turborepo Fast builds, remote caching, simple config, Vercel integration Modern apps, Vercel users Low-Medium
Lerna Package versioning, publishing, bootstrap, legacy support Library authors, npm packages Medium
npm/yarn/pnpm workspaces Built-in, simple, no extra tools Small-medium projects Low

Example: Nx monorepo setup with React and shared libraries

// Initialize Nx workspace
npx create-nx-workspace@latest myorg

// Structure
myorg/
├── apps/
   ├── web/                    # Next.js app
   ├── mobile/                 # React Native app
   └── admin/                  # Admin dashboard
├── libs/
   ├── shared/
   ├── ui/                 # Shared UI components
   ├── utils/              # Utility functions
   └── types/              # TypeScript types
   ├── features/
   ├── auth/               # Authentication feature
   └── payments/           # Payment feature
   └── data-access/
       └── api/                # API client
├── tools/                      # Custom scripts
├── nx.json                     # Nx configuration
├── tsconfig.base.json          # Base TypeScript config
└── package.json

// nx.json - Configuration
{
  "tasksRunnerOptions": {
    "default": {
      "runner": "nx/tasks-runners/default",
      "options": {
        "cacheableOperations": ["build", "test", "lint"],
        "parallel": 3
      }
    }
  },
  "targetDefaults": {
    "build": {
      "dependsOn": ["^build"],
      "cache": true
    }
  }
}

// tsconfig.base.json - Path mapping
{
  "compilerOptions": {
    "paths": {
      "@myorg/shared/ui": ["libs/shared/ui/src/index.ts"],
      "@myorg/shared/utils": ["libs/shared/utils/src/index.ts"],
      "@myorg/features/auth": ["libs/features/auth/src/index.ts"]
    }
  }
}

// Generate new library
nx generate @nx/react:library shared/ui

// Generate new app
nx generate @nx/next:application admin

// Run commands
nx build web                    # Build web app
nx test shared-ui               # Test UI library
nx run-many --target=test --all # Test all projects
nx affected:build               # Build only affected by changes
nx dep-graph                    # Visualize dependency graph

Example: Turborepo setup (simpler alternative)

// Initialize Turborepo
npx create-turbo@latest

// Structure
my-turborepo/
├── apps/
│   ├── web/                    # Next.js app
│   └── docs/                   # Documentation site
├── packages/
│   ├── ui/                     # Shared UI components
│   ├── eslint-config/          # Shared ESLint config
│   ├── tsconfig/               # Shared TS configs
│   └── typescript-config/
├── turbo.json                  # Turbo configuration
└── package.json

// turbo.json - Pipeline configuration
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "cache": false
    },
    "lint": {
      "outputs": []
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

// Root package.json
{
  "name": "my-turborepo",
  "private": true,
  "workspaces": ["apps/*", "packages/*"],
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "test": "turbo run test",
    "lint": "turbo run lint"
  },
  "devDependencies": {
    "turbo": "latest"
  }
}

// packages/ui/package.json
{
  "name": "@repo/ui",
  "version": "0.0.0",
  "main": "./src/index.tsx",
  "types": "./src/index.tsx",
  "exports": {
    ".": "./src/index.tsx",
    "./button": "./src/Button.tsx"
  }
}

// apps/web - Import from workspace
import { Button } from '@repo/ui';
import { formatDate } from '@repo/utils';

Example: pnpm workspaces (lightweight approach)

// pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'

// Root package.json
{
  "name": "monorepo",
  "private": true,
  "scripts": {
    "build": "pnpm -r --filter='./packages/*' build",
    "test": "pnpm -r test",
    "dev": "pnpm --parallel -r dev"
  }
}

// packages/shared/package.json
{
  "name": "@monorepo/shared",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch"
  }
}

// apps/web/package.json
{
  "name": "web",
  "dependencies": {
    "@monorepo/shared": "workspace:*"
  }
}

// Commands
pnpm install                    # Install all deps
pnpm --filter web dev           # Run web app
pnpm --filter @monorepo/shared build  # Build shared package
pnpm -r build                   # Build all packages recursively
Choosing a Tool: Use Nx for large teams with complex dependencies. Use Turborepo for modern apps with simple needs. Use pnpm workspaces for minimal overhead. Use Lerna for publishing npm packages.

15.2 Feature-based Folder Structure

Organize code by feature/domain rather than technical layer for better scalability and team collaboration.

Structure Type Organization Pros Cons
Feature-based Group by business feature Scalable, clear ownership, easy to delete Shared code requires careful planning
Layer-based Group by technical layer (components, hooks, utils) Simple, familiar, flat structure Hard to scale, unclear ownership
Hybrid Features + shared folder for common code Best of both worlds More complex initially
src/
├── features/
│   ├── auth/
│   │   ├── components/
│   │   │   ├── LoginForm.tsx
│   │   │   ├── SignupForm.tsx
│   │   │   └── PasswordReset.tsx
│   │   ├── hooks/
│   │   │   ├── useAuth.ts
│   │   │   └── useLogin.ts
│   │   ├── api/
│   │   │   └── authApi.ts
│   │   ├── types/
│   │   │   └── auth.types.ts
│   │   ├── utils/
│   │   │   └── validation.ts
│   │   └── index.ts              # Barrel export
│   │
│   ├── dashboard/
│   │   ├── components/
│   │   │   ├── DashboardLayout.tsx
│   │   │   ├── StatsCard.tsx
│   │   │   └── RecentActivity.tsx
│   │   ├── hooks/
│   │   │   └── useDashboardData.ts
│   │   ├── api/
│   │   │   └── dashboardApi.ts
│   │   └── index.ts
│   │
│   ├── products/
│   │   ├── components/
│   │   │   ├── ProductList.tsx
│   │   │   ├── ProductCard.tsx
│   │   │   └── ProductDetail.tsx
│   │   ├── hooks/
│   │   │   ├── useProducts.ts
│   │   │   └── useProductFilters.ts
│   │   ├── api/
│   │   │   └── productsApi.ts
│   │   ├── store/
│   │   │   └── productsSlice.ts
│   │   └── index.ts
│   │
│   └── cart/
│       ├── components/
│       ├── hooks/
│       ├── api/
│       └── index.ts

├── shared/
│   ├── components/              # Reusable UI components
│   │   ├── Button/
│   │   ├── Input/
│   │   ├── Modal/
│   │   └── Layout/
│   ├── hooks/                   # Generic hooks
│   │   ├── useDebounce.ts
│   │   ├── useLocalStorage.ts
│   │   └── useMediaQuery.ts
│   ├── utils/                   # Generic utilities
│   │   ├── format.ts
│   │   ├── validation.ts
│   │   └── api.ts
│   ├── types/                   # Global types
│   │   └── common.types.ts
│   └── constants/
│       └── config.ts

├── pages/                       # Next.js pages or React Router routes
│   ├── index.tsx
│   ├── dashboard.tsx
│   └── products/
│       ├── [id].tsx
│       └── index.tsx

├── styles/                      # Global styles
│   ├── globals.css
│   └── variables.css

├── lib/                         # External library configs
│   ├── axios.ts
│   └── react-query.ts

└── App.tsx
// Colocate tests, stories, styles with components
src/features/auth/components/LoginForm/
├── LoginForm.tsx
├── LoginForm.test.tsx
├── LoginForm.stories.tsx
├── LoginForm.module.css
├── useLoginForm.ts              # Component-specific hook
└── index.ts

// LoginForm/index.ts - Barrel export
export { LoginForm } from './LoginForm';
export type { LoginFormProps } from './LoginForm';

// Usage in other files
import { LoginForm } from '@/features/auth/components/LoginForm';

// Alternative flat structure for simple components
src/features/auth/components/
├── LoginForm.tsx
├── LoginForm.test.tsx
├── LoginForm.stories.tsx
├── SignupForm.tsx
└── SignupForm.test.tsx

Example: Next.js App Router with features

app/
├── (auth)/                      # Route group
│   ├── login/
│   │   └── page.tsx             # Uses LoginForm from features/auth
│   └── signup/
│       └── page.tsx

├── dashboard/
│   ├── page.tsx                 # Uses Dashboard from features/dashboard
│   ├── layout.tsx
│   └── settings/
│       └── page.tsx

├── products/
│   ├── page.tsx
│   └── [id]/
│       └── page.tsx

├── api/
│   ├── auth/
│   │   └── route.ts
│   └── products/
│       └── route.ts

├── layout.tsx
└── page.tsx

// Separation of concerns:
// - features/ = business logic, UI, hooks
// - app/ = routing, server components, metadata
// - lib/ = configurations, clients
Anti-pattern: Don't create deep nesting (max 3-4 levels). Don't mix feature code with shared code. Don't use index.ts for everything (explicit imports are clearer). Avoid circular dependencies between features.

15.3 Barrel Exports Index Files

Use index.ts files to create clean public APIs for modules and simplify imports, but avoid performance pitfalls.

Pattern Use Case Benefits Cautions
Feature Barrel Export feature public API Clean imports, encapsulation Can break tree-shaking
Component Barrel Export component + types Single import point Import cost for all
Namespace Barrel Group related utilities Organized exports All-or-nothing imports
Direct Exports Performance-critical code Best tree-shaking Longer import paths

Example: Feature barrel export pattern

// features/auth/index.ts - Public API
export { LoginForm } from './components/LoginForm';
export { SignupForm } from './components/SignupForm';
export { useAuth } from './hooks/useAuth';
export { useLogin } from './hooks/useLogin';
export type { User, AuthState, LoginCredentials } from './types/auth.types';

// Don't export internal utilities
// import { validateEmail } from './utils/validation'; ❌

// Consumer code
import { LoginForm, useAuth } from '@/features/auth';

// features/auth/components/index.ts - Component barrel
export { LoginForm } from './LoginForm/LoginForm';
export { SignupForm } from './SignupForm/SignupForm';
export { PasswordReset } from './PasswordReset/PasswordReset';

// Usage
import { LoginForm, SignupForm } from '@/features/auth/components';

Example: Tree-shaking friendly exports

// ❌ Bad - Breaks tree-shaking (imports everything)
// shared/utils/index.ts
export * from './format';
export * from './validation';
export * from './api';
export * from './date';

// Usage imports ALL utils even if only using one
import { formatCurrency } from '@/shared/utils';

// ✅ Good - Named exports (better tree-shaking)
// shared/utils/index.ts
export { formatCurrency, formatDate } from './format';
export { validateEmail, validatePhone } from './validation';
export { apiClient, handleApiError } from './api';

// ✅ Better - Direct imports (best tree-shaking)
import { formatCurrency } from '@/shared/utils/format';
import { validateEmail } from '@/shared/utils/validation';

// ✅ Best - Namespace exports for related functions
// shared/utils/format/index.ts
export const format = {
  currency: (value: number) => `${value.toFixed(2)}`,
  date: (date: Date) => date.toLocaleDateString(),
  percent: (value: number) => `${(value * 100).toFixed(2)}%`
};

// Usage
import { format } from '@/shared/utils/format';
format.currency(100); // "$100.00"

Example: Component library barrel exports

// shared/components/index.ts - Component library
export { Button } from './Button';
export { Input } from './Input';
export { Modal } from './Modal';
export { Card } from './Card';
export type { ButtonProps } from './Button';
export type { InputProps } from './Input';

// Usage
import { Button, Card, type ButtonProps } from '@/shared/components';

// Alternative: Subpath exports in package.json
// package.json
{
  "exports": {
    ".": "./src/index.ts",
    "./button": "./src/Button.tsx",
    "./input": "./src/Input.tsx",
    "./modal": "./src/Modal.tsx"
  }
}

// Usage with subpaths (better performance)
import { Button } from '@repo/ui/button';
import { Input } from '@repo/ui/input';
Best Practice: Use barrel exports for feature boundaries and component libraries. Use direct imports for utilities and helpers. Configure sideEffects: false in package.json for better tree-shaking.

15.4 Absolute Imports Path Mapping

Configure absolute imports to avoid relative path hell and improve code readability with TypeScript path mapping.

Configuration Tool/Framework Config File Syntax
TypeScript Paths All TS projects tsconfig.json @/*, @components/*
Webpack Aliases CRA, custom webpack webpack.config.js resolve.alias
Next.js Next.js projects tsconfig.json (auto) @/* by default
Vite Vite projects vite.config.ts resolve.alias

Example: TypeScript path mapping configuration

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/shared/components/*"],
      "@features/*": ["src/features/*"],
      "@hooks/*": ["src/shared/hooks/*"],
      "@utils/*": ["src/shared/utils/*"],
      "@types/*": ["src/shared/types/*"],
      "@lib/*": ["src/lib/*"],
      "@styles/*": ["src/styles/*"]
    }
  }
}

// Before (relative imports - hard to read)
import Button from '../../../shared/components/Button';
import { formatDate } from '../../../../shared/utils/format';
import { useAuth } from '../../features/auth/hooks/useAuth';

// After (absolute imports - clean and clear)
import Button from '@components/Button';
import { formatDate } from '@utils/format';
import { useAuth } from '@features/auth/hooks/useAuth';

// Alternative: Single @ prefix
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

// Usage
import Button from '@/shared/components/Button';
import { formatDate } from '@/shared/utils/format';
import { useAuth } from '@/features/auth/hooks/useAuth';

Example: Vite configuration with aliases

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@components': path.resolve(__dirname, './src/shared/components'),
      '@features': path.resolve(__dirname, './src/features'),
      '@hooks': path.resolve(__dirname, './src/shared/hooks'),
      '@utils': path.resolve(__dirname, './src/shared/utils'),
      '@types': path.resolve(__dirname, './src/shared/types'),
      '@styles': path.resolve(__dirname, './src/styles')
    }
  }
});

// tsconfig.json (must match Vite config)
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/shared/components/*"],
      "@features/*": ["src/features/*"],
      "@hooks/*": ["src/shared/hooks/*"],
      "@utils/*": ["src/shared/utils/*"],
      "@types/*": ["src/shared/types/*"],
      "@styles/*": ["src/styles/*"]
    }
  }
}

Example: Next.js automatic path mapping

// tsconfig.json - Next.js auto-configures @/* alias
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@/components/*": ["./src/components/*"],
      "@/lib/*": ["./src/lib/*"]
    }
  }
}

// Usage in Next.js
// app/page.tsx
import { Button } from '@/components/ui/Button';
import { db } from '@/lib/database';

// Monorepo with multiple apps
// apps/web/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "paths": {
      "@web/*": ["./src/*"],
      "@repo/ui": ["../../packages/ui/src/index.ts"],
      "@repo/utils": ["../../packages/utils/src/index.ts"]
    }
  }
}

Example: Jest configuration for path mapping

// jest.config.js - Match TypeScript paths
module.exports = {
  moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/src/$1",
    "^@components/(.*)$": "<rootDir>/src/shared/components/$1",
    "^@features/(.*)$": "<rootDir>/src/features/$1",
    "^@hooks/(.*)$": "<rootDir>/src/shared/hooks/$1",
    "^@utils/(.*)$": "<rootDir>/src/shared/utils/$1"
  }
};

// Alternative: Use ts-jest preset (auto-reads tsconfig paths)
module.exports = {
  preset: 'ts-jest',
  moduleNameMapper: {
    "^@/(.*)\\$": "<rootDir>/src/$1"
  }
};
Common Pitfalls: Ensure build tools (Webpack, Vite, Jest) configurations match tsconfig.json paths. Use relative imports for files in same feature. Don't mix @ and ~ prefixes. Keep path mappings simple (2-3 max).

15.5 ESLint Prettier Husky Configuration

Enforce code quality and consistency with automated linting, formatting, and pre-commit hooks.

Tool Purpose When It Runs Config File
ESLint Find code quality issues, enforce rules On save, pre-commit, CI .eslintrc.json
Prettier Format code consistently On save, pre-commit .prettierrc
Husky Git hooks automation Pre-commit, pre-push .husky/
lint-staged Run linters on staged files only Pre-commit .lintstagedrc

Example: Complete ESLint + Prettier + Husky setup

// Installation
npm install -D eslint prettier eslint-config-prettier eslint-plugin-prettier
npm install -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm install -D eslint-plugin-react eslint-plugin-react-hooks
npm install -D husky lint-staged

// Initialize Husky
npx husky init

// .eslintrc.json
{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "prettier" // Must be last to override other configs
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2022,
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "plugins": ["@typescript-eslint", "react", "react-hooks", "prettier"],
  "rules": {
    "prettier/prettier": "error",
    "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
    "@typescript-eslint/explicit-module-boundary-types": "off",
    "react/react-in-jsx-scope": "off", // Not needed in React 17+
    "react/prop-types": "off", // Using TypeScript
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn"
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  }
}

// .prettierrc
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "arrowParens": "avoid",
  "endOfLine": "lf"
}

// .prettierignore
node_modules
.next
dist
build
coverage
*.min.js

Example: Husky + lint-staged configuration

// package.json
{
  "scripts": {
    "lint": "eslint . --ext .ts,.tsx,.js,.jsx",
    "lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix",
    "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
    "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
    "prepare": "husky"
  },
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,css,md}": [
      "prettier --write"
    ]
  }
}

// .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

// .husky/pre-push
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run type-check
npm run test

// .lintstagedrc.js (alternative config file)
module.exports = {
  '*.{ts,tsx}': [
    'eslint --fix',
    'prettier --write',
    () => 'tsc --noEmit' // Type check all files
  ],
  '*.{js,jsx}': ['eslint --fix', 'prettier --write'],
  '*.{json,md,css}': ['prettier --write']
};

Example: Next.js ESLint configuration

// .eslintrc.json - Next.js specific
{
  "extends": [
    "next/core-web-vitals",
    "plugin:@typescript-eslint/recommended",
    "prettier"
  ],
  "rules": {
    "@next/next/no-html-link-for-pages": "off",
    "@typescript-eslint/no-explicit-any": "warn",
    "@typescript-eslint/no-unused-vars": ["error", { 
      "argsIgnorePattern": "^_",
      "varsIgnorePattern": "^_"
    }]
  }
}

// VS Code settings.json - Auto-format on save
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ]
}
Best Practice: Run prettier through ESLint with eslint-plugin-prettier. Use lint-staged to only check changed files (faster). Configure editor to format on save. Add type-check to pre-push hook.

15.6 TypeScript Strict Mode Configuration

Enable TypeScript strict mode for maximum type safety and catch errors at compile-time instead of runtime.

Strict Option What It Does Common Issues It Catches
strict Enables all strict checks (recommended) All type-related issues
strictNullChecks null/undefined must be explicit Null pointer exceptions
strictFunctionTypes Strict function parameter checking Function type mismatches
noImplicitAny No implicit any types Missing type annotations
noUnusedLocals Error on unused variables Dead code, typos
noImplicitReturns All code paths must return Missing return statements

Example: Strict TypeScript configuration

// tsconfig.json - Recommended strict settings
{
  "compilerOptions": {
    // Strict Type Checking
    "strict": true,                           // Enable all strict options
    "noImplicitAny": true,                    // No implicit 'any'
    "strictNullChecks": true,                 // Strict null/undefined checks
    "strictFunctionTypes": true,              // Strict function types
    "strictBindCallApply": true,              // Strict bind/call/apply
    "strictPropertyInitialization": true,     // Class properties must be initialized
    "noImplicitThis": true,                   // No implicit 'this'
    "alwaysStrict": true,                     // Parse in strict mode
    
    // Additional Checks
    "noUnusedLocals": true,                   // Error on unused variables
    "noUnusedParameters": true,               // Error on unused parameters
    "noImplicitReturns": true,                // All paths must return
    "noFallthroughCasesInSwitch": true,       // No fallthrough in switch
    "noUncheckedIndexedAccess": true,         // Index signatures return T | undefined
    "allowUnreachableCode": false,            // Error on unreachable code
    
    // Module Resolution
    "moduleResolution": "bundler",            // Modern resolution
    "resolveJsonModule": true,                // Import JSON files
    "isolatedModules": true,                  // Each file as separate module
    "esModuleInterop": true,                  // Better CommonJS interop
    "skipLibCheck": true,                     // Skip type checking .d.ts files
    
    // Emit
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "jsx": "react-jsx",                       // React 17+ JSX transform
    "module": "ESNext",
    "sourceMap": true,
    
    // Path Mapping
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist", "build"]
}

Example: Strict null checks and proper typing

// ❌ Without strictNullChecks
function getUser(id: string) {
  const user = database.findUser(id); // Could be undefined
  return user.name; // Runtime error if user is undefined
}

// ✅ With strictNullChecks
function getUser(id: string): string | undefined {
  const user = database.findUser(id); // User | undefined
  
  // Must handle null/undefined explicitly
  if (!user) {
    return undefined;
  }
  
  return user.name;
}

// ✅ Better - Use optional chaining
function getUserName(id: string): string | undefined {
  const user = database.findUser(id);
  return user?.name; // Safe navigation
}

// ✅ Best - Use nullish coalescing
function getUserNameOrDefault(id: string): string {
  const user = database.findUser(id);
  return user?.name ?? 'Anonymous';
}

// ❌ noImplicitAny error
function process(data) { // Error: Parameter 'data' implicitly has 'any' type
  return data.value;
}

// ✅ Fixed with explicit type
function process(data: { value: string }): string {
  return data.value;
}

// ✅ Generic for reusability
function process<T>(data: { value: T }): T {
  return data.value;
}

Example: Class strictPropertyInitialization

// ❌ Without strictPropertyInitialization
class User {
  name: string; // No error, but undefined at runtime
  email: string;
}

// ✅ Fixed - Initialize in constructor
class User {
  name: string;
  email: string;
  
  constructor(name: string, email: string) {
    this.name = name;
    this.email = email;
  }
}

// ✅ Alternative - Definite assignment assertion (use carefully)
class User {
  name!: string; // ! tells TS we'll initialize it
  email!: string;
  
  async init(id: string) {
    const data = await fetchUser(id);
    this.name = data.name;
    this.email = data.email;
  }
}

// ✅ Best - Use optional or union types
class User {
  name?: string;           // Explicitly optional
  email: string | null;    // Explicitly nullable
  
  constructor(email: string | null) {
    this.email = email;
  }
}

// React component with strict mode
interface Props {
  user?: User;  // Explicitly optional
  onSave: (data: User) => void;
}

function UserProfile({ user, onSave }: Props) {
  if (!user) {
    return <div>No user data</div>;
  }
  
  // TypeScript knows user is defined here
  return <div>{user.name}</div>;
}

Example: noUncheckedIndexedAccess

// Without noUncheckedIndexedAccess
const colors = ['red', 'green', 'blue'];
const color = colors[5]; // color: string (wrong!)
console.log(color.toUpperCase()); // Runtime error

// ✅ With noUncheckedIndexedAccess enabled
const colors = ['red', 'green', 'blue'];
const color = colors[5]; // color: string | undefined (correct!)

if (color) {
  console.log(color.toUpperCase()); // Safe
}

// Dictionary access
interface UserMap {
  [id: string]: User;
}

const users: UserMap = { '1': { name: 'John' } };

// Without noUncheckedIndexedAccess
const user = users['999']; // user: User (wrong!)

// With noUncheckedIndexedAccess
const user = users['999']; // user: User | undefined (correct!)
if (user) {
  console.log(user.name);
}

Project Structure Best Practices

Aspect Recommendation Rationale
Monorepo Nx (enterprise), Turborepo (modern), pnpm (simple) Choose based on team size and complexity
Folder Structure Feature-based with shared folder Scalable, clear ownership, easy refactoring
Barrel Exports Use for features, avoid for utilities Clean API, but watch bundle size
Path Mapping @ prefix with 2-3 aliases max Clean imports without path hell
Code Quality ESLint + Prettier + Husky + lint-staged Automated consistency, catch errors early
TypeScript strict: true with all checks enabled Maximum type safety, fewer runtime errors

16. Development Workflow Implementation Tools

16.1 Vite Hot Module Replacement

Lightning-fast HMR with instant updates without full page reload, preserving application state during development.

Feature Description Benefit Use Case
Fast Refresh Updates React components without losing state Instant feedback Component development
CSS HMR Update styles without reload Live style changes UI/UX development
import.meta.hot API Fine-grained HMR control Custom module updates Advanced scenarios
Dependency Pre-bundling ESBuild pre-bundles node_modules Fast cold start Large dependencies

Example: Vite configuration with HMR

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [
    react({
      // Enable Fast Refresh
      fastRefresh: true,
      // Babel options for custom transforms
      babel: {
        plugins: ['babel-plugin-styled-components']
      }
    })
  ],
  server: {
    port: 3000,
    open: true,
    hmr: {
      // HMR configuration
      overlay: true, // Show errors in browser overlay
      port: 3000
    },
    // Watch options
    watch: {
      usePolling: false, // Use native file watchers
      ignored: ['**/node_modules/**', '**/.git/**']
    }
  },
  // Optimize dependencies
  optimizeDeps: {
    include: ['react', 'react-dom'],
    exclude: ['@your-lib/package']
  }
});

// Custom HMR handling in module
// utils/config.ts
export const config = {
  apiUrl: 'http://localhost:8000',
  timeout: 5000
};

// Accept HMR updates for this module
if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    console.log('Config updated:', newModule);
  });
}

Example: React Fast Refresh with state preservation

// Counter.tsx - State is preserved during HMR
import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(c => c + 1)}>
        Increment
      </button>
      {/* Change this text - count state is preserved! */}
      <p>Click the button to count</p>
    </div>
  );
}

// HMR Boundary - Force reload on certain changes
// AppProvider.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

export function AppProvider({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );
}

// Force full reload if this module changes
if (import.meta.hot) {
  import.meta.hot.dispose(() => {
    // Cleanup before reload
    queryClient.clear();
  });
}

Example: Vite environment variables with HMR

// .env.development
VITE_API_URL=http://localhost:8000
VITE_APP_NAME=My App Dev

// Access in code (HMR updates automatically)
export const config = {
  apiUrl: import.meta.env.VITE_API_URL,
  appName: import.meta.env.VITE_APP_NAME,
  isDev: import.meta.env.DEV,
  isProd: import.meta.env.PROD
};

// TypeScript types for env variables
/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_API_URL: string;
  readonly VITE_APP_NAME: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

// Conditional logic with HMR
if (import.meta.env.DEV) {
  console.log('Development mode - HMR enabled');
}
Performance: Vite HMR is 10-100x faster than Webpack due to native ES modules and esbuild. Cold start in ~500ms, updates in <50ms. Use vite-plugin-inspect to debug HMR issues.

16.2 Webpack Dev Server Proxy

Configure development proxy to bypass CORS issues and route API requests to backend server during local development.

Proxy Type Configuration Use Case Benefits
Simple Proxy String target URL Single backend API Quick setup, bypass CORS
Path Rewrite Modify request paths Different API versioning Flexible routing
Multiple Proxies Object with multiple targets Microservices Route to different services
WebSocket Proxy ws: true option Real-time connections WebSocket support

Example: Webpack Dev Server proxy configuration

// webpack.config.js
module.exports = {
  devServer: {
    port: 3000,
    // Simple proxy - forward /api to backend
    proxy: {
      '/api': 'http://localhost:8000'
    }
  }
};

// Request: http://localhost:3000/api/users
// Proxied to: http://localhost:8000/api/users

// Advanced proxy with options
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        pathRewrite: { '^/api': '' }, // Remove /api prefix
        changeOrigin: true, // Change origin header
        secure: false, // Accept self-signed certificates
        logLevel: 'debug' // Log proxy requests
      },
      // Multiple backend services
      '/auth': {
        target: 'http://localhost:8001',
        changeOrigin: true
      },
      '/graphql': {
        target: 'http://localhost:8002',
        changeOrigin: true
      },
      // WebSocket proxy
      '/ws': {
        target: 'ws://localhost:8003',
        ws: true, // Enable WebSocket proxying
        changeOrigin: true
      }
    }
  }
};

// Request: http://localhost:3000/api/users
// Proxied to: http://localhost:8000/users (api removed)

Example: Vite proxy configuration

// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    proxy: {
      // Simple proxy
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      },
      // Proxy with custom headers
      '/auth': {
        target: 'http://localhost:8001',
        changeOrigin: true,
        configure: (proxy, options) => {
          proxy.on('proxyReq', (proxyReq, req, res) => {
            // Add custom headers
            proxyReq.setHeader('X-Custom-Header', 'value');
            console.log('Proxying:', req.method, req.url);
          });
          proxy.on('proxyRes', (proxyRes, req, res) => {
            console.log('Response:', proxyRes.statusCode, req.url);
          });
        }
      },
      // Conditional proxy based on request
      '^/api/v[0-9]+': {
        target: 'http://localhost:8000',
        changeOrigin: true,
        rewrite: (path) => {
          // /api/v1/users -> /v1/users
          return path.replace(/^\/api/, '');
        }
      }
    }
  }
});

// Next.js proxy (next.config.js)
module.exports = {
  async rewrites() {
    return [
      {
        source: '/api/:path*',
        destination: 'http://localhost:8000/:path*'
      }
    ];
  }
};

Example: Create React App proxy setup

// package.json - Simple proxy
{
  "proxy": "http://localhost:8000"
}

// All requests to unknown routes are proxied to backend

// src/setupProxy.js - Advanced proxy
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  // API proxy
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://localhost:8000',
      changeOrigin: true,
      pathRewrite: {
        '^/api': '' // Remove /api prefix
      },
      onProxyReq: (proxyReq, req, res) => {
        console.log('Proxying:', req.method, req.url);
      }
    })
  );
  
  // Auth service proxy
  app.use(
    '/auth',
    createProxyMiddleware({
      target: 'http://localhost:8001',
      changeOrigin: true
    })
  );
  
  // WebSocket proxy
  app.use(
    '/ws',
    createProxyMiddleware({
      target: 'http://localhost:8003',
      ws: true,
      changeOrigin: true
    })
  );
};
Security Note: Proxy configuration only works in development. In production, configure proper CORS headers on backend or use same-origin deployment. Never expose sensitive backend URLs in client code.

16.3 GitHub Actions CI CD Pipeline

Automate testing, building, and deployment with GitHub Actions for continuous integration and delivery.

Workflow Stage Actions Purpose Triggers
CI (Continuous Integration) Lint, test, type-check, build Validate code quality Push, PR
CD (Continuous Deployment) Build, deploy to staging/prod Automated deployment Merge to main
Scheduled Jobs Security audits, dependency updates Maintenance tasks Cron schedule
Release Version bump, changelog, publish Package release Tag creation

Example: Complete CI/CD workflow for React app

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [18.x, 20.x]
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Lint
        run: npm run lint
      
      - name: Type check
        run: npm run type-check
      
      - name: Run tests
        run: npm run test:ci
      
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/coverage-final.json
          fail_ci_if_error: true
  
  build:
    runs-on: ubuntu-latest
    needs: lint-and-test
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: '20.x'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
        env:
          NODE_ENV: production
      
      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: build-output
          path: dist/
          retention-days: 7

Example: Deployment workflow to Vercel/Netlify

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: '20.x'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
        env:
          VITE_API_URL: ${{ secrets.PROD_API_URL }}
          VITE_APP_NAME: 'Production App'
      
      # Deploy to Vercel
      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod'
      
      # Or deploy to Netlify
      - name: Deploy to Netlify
        uses: nwtgck/actions-netlify@v2
        with:
          publish-dir: './dist'
          production-deploy: true
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
      
      - name: Notify Slack
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          text: 'Deployment to production completed'
          webhook_url: ${{ secrets.SLACK_WEBHOOK }}
        if: always()

Example: Monorepo CI with Turborepo

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2 # For turbo to detect changes
      
      - uses: actions/setup-node@v4
        with:
          node-version: '20.x'
      
      - name: Install pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8
      
      - name: Get pnpm store directory
        id: pnpm-cache
        run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
      
      - name: Setup pnpm cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-
      
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
      
      - name: Build
        run: pnpm turbo run build
      
      - name: Test
        run: pnpm turbo run test
      
      - name: Lint
        run: pnpm turbo run lint

Example: Automated dependency updates with Dependabot

# .github/dependabot.yml
version: 2
updates:
  # Enable npm dependency updates
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
    open-pull-requests-limit: 10
    reviewers:
      - "team-frontend"
    labels:
      - "dependencies"
      - "automated"
    # Group minor/patch updates
    groups:
      production-dependencies:
        patterns:
          - "*"
        update-types:
          - "minor"
          - "patch"
  
  # GitHub Actions updates
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

# Auto-merge Dependabot PRs (workflow)
# .github/workflows/dependabot-auto-merge.yml
name: Dependabot auto-merge
on: pull_request

permissions:
  pull-requests: write
  contents: write

jobs:
  dependabot:
    runs-on: ubuntu-latest
    if: github.actor == 'dependabot[bot]'
    steps:
      - name: Dependabot metadata
        id: metadata
        uses: dependabot/fetch-metadata@v1
      
      - name: Enable auto-merge for Dependabot PRs
        if: steps.metadata.outputs.update-type == 'version-update:semver-patch'
        run: gh pr merge --auto --squash "$PR_URL"
        env:
          PR_URL: ${{github.event.pull_request.html_url}}
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
Best Practice: Cache node_modules and build outputs for faster CI. Run jobs in parallel when possible. Use matrix strategy for multi-version testing. Set up branch protection rules requiring CI pass.

16.4 Pre-commit Hooks lint-staged

Run linters and formatters only on staged files before commit to catch issues early and maintain code quality.

Tool Purpose When Runs Performance
Husky Git hooks management Various git events Instant hook registration
lint-staged Run commands on staged files only Pre-commit Fast (only changed files)
commitlint Validate commit messages Commit-msg hook Instant validation
pre-push Run tests before push Pre-push Can be slow (full tests)

Example: Complete pre-commit hooks setup

// Installation
npm install -D husky lint-staged @commitlint/cli @commitlint/config-conventional

// Initialize Husky
npx husky init

// package.json
{
  "scripts": {
    "prepare": "husky",
    "lint": "eslint .",
    "format": "prettier --write .",
    "type-check": "tsc --noEmit",
    "test": "vitest"
  },
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,md,css,scss}": [
      "prettier --write"
    ],
    "*.{ts,tsx}": [
      () => "tsc --noEmit" // Type check all files, not just staged
    ]
  }
}

// .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

// .husky/commit-msg
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no -- commitlint --edit $1

// .husky/pre-push
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run type-check
npm run test -- --run

// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat',     // New feature
        'fix',      // Bug fix
        'docs',     // Documentation
        'style',    // Formatting
        'refactor', // Code restructuring
        'test',     // Tests
        'chore',    // Maintenance
        'perf',     // Performance
        'ci',       // CI/CD
        'build',    // Build system
        'revert'    // Revert commit
      ]
    ],
    'subject-case': [2, 'always', 'sentence-case']
  }
};

// Valid commit messages:
// feat: add user authentication
// fix: resolve navigation bug
// docs: update README installation steps
// added new feature (missing type)
// Fix: bug (wrong case)

Example: Advanced lint-staged configuration

// .lintstagedrc.js - More control
module.exports = {
  // TypeScript/JavaScript files
  '*.{ts,tsx}': [
    'eslint --fix --max-warnings=0',
    'prettier --write',
    () => 'tsc --noEmit --pretty' // Run for all files
  ],
  
  // JavaScript files
  '*.{js,jsx}': [
    'eslint --fix --max-warnings=0',
    'prettier --write'
  ],
  
  // Styles
  '*.{css,scss}': [
    'stylelint --fix',
    'prettier --write'
  ],
  
  // JSON/Markdown
  '*.{json,md}': ['prettier --write'],
  
  // Test files - run related tests
  '**/*.test.{ts,tsx}': [
    (filenames) => {
      const tests = filenames.map(f => `--testPathPattern=${f}`).join(' ');
      return `vitest run ${tests}`;
    }
  ],
  
  // Package.json - validate and sort
  'package.json': [
    'sort-package-json',
    'prettier --write'
  ]
};

// Skip hooks when needed
// git commit --no-verify -m "emergency fix"
// git push --no-verify

Example: Monorepo lint-staged with Turborepo

// Root .lintstagedrc.js
module.exports = {
  '*': 'prettier --write --ignore-unknown',
  '*.{ts,tsx,js,jsx}': (filenames) => {
    // Only lint files in affected packages
    const packages = new Set(
      filenames.map(f => f.split('/')[1]) // Extract package name
    );
    
    return Array.from(packages).map(
      pkg => `turbo run lint --filter=./${pkg}`
    );
  }
};

// Per-package hooks
// apps/web/.lintstagedrc.js
module.exports = {
  '*.{ts,tsx}': [
    'eslint --fix',
    'prettier --write',
    () => 'tsc --noEmit -p apps/web'
  ]
};

// Root package.json
{
  "scripts": {
    "prepare": "husky"
  },
  "lint-staged": {
    "apps/**/*.{ts,tsx}": [
      "turbo run lint --filter=./apps/*"
    ],
    "packages/**/*.{ts,tsx}": [
      "turbo run lint --filter=./packages/*"
    ]
  }
}
Performance Tip: Avoid running full test suite in pre-commit (too slow). Use pre-push for tests. For type-checking, use () => 'tsc --noEmit' to check all files, not individual staged files.

16.5 Storybook Component Documentation

Develop, document, and test UI components in isolation with comprehensive documentation and interactive examples.

Feature Purpose Benefit Add-on
Component Isolation Develop without full app Faster iteration Built-in
Interactive Docs Auto-generate documentation Living style guide @storybook/addon-docs
Controls Modify props in UI Test all states @storybook/addon-controls
Actions Log event handlers Debug interactions @storybook/addon-actions
Accessibility A11y testing Catch issues early @storybook/addon-a11y

Example: Storybook setup and configuration

// Installation
npx storybook@latest init

// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/addon-a11y',
    '@storybook/addon-coverage'
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {}
  },
  docs: {
    autodocs: 'tag' // Auto-generate docs page
  }
};

export default config;

// .storybook/preview.ts
import type { Preview } from '@storybook/react';
import '../src/styles/globals.css';

const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i
      }
    },
    backgrounds: {
      default: 'light',
      values: [
        { name: 'light', value: '#ffffff' },
        { name: 'dark', value: '#1a1a1a' }
      ]
    }
  }
};

export default preview;

Example: Component stories with all variants

// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'danger']
    },
    size: {
      control: 'radio',
      options: ['sm', 'md', 'lg']
    },
    onClick: { action: 'clicked' }
  }
};

export default meta;
type Story = StoryObj<typeof Button>;

// Default story
export const Primary: Story = {
  args: {
    variant: 'primary',
    children: 'Button'
  }
};

// Variants
export const Secondary: Story = {
  args: {
    variant: 'secondary',
    children: 'Button'
  }
};

export const Danger: Story = {
  args: {
    variant: 'danger',
    children: 'Delete'
  }
};

// Sizes
export const Small: Story = {
  args: {
    size: 'sm',
    children: 'Small Button'
  }
};

export const Large: Story = {
  args: {
    size: 'lg',
    children: 'Large Button'
  }
};

// States
export const Disabled: Story = {
  args: {
    disabled: true,
    children: 'Disabled'
  }
};

export const Loading: Story = {
  args: {
    loading: true,
    children: 'Loading...'
  }
};

// With icons
export const WithIcon: Story = {
  args: {
    children: (
      <>
        <span>🚀</span> Launch
      </>
    )
  }
};

Example: Interactive stories with play function

// LoginForm.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { within, userEvent, expect } from '@storybook/test';
import { LoginForm } from './LoginForm';

const meta: Meta<typeof LoginForm> = {
  title: 'Features/LoginForm',
  component: LoginForm,
  parameters: {
    layout: 'centered'
  }
};

export default meta;
type Story = StoryObj<typeof LoginForm>;

// Basic story
export const Default: Story = {};

// Interactive test
export const FilledForm: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    
    // Find and fill inputs
    const emailInput = canvas.getByLabelText('Email');
    const passwordInput = canvas.getByLabelText('Password');
    const submitButton = canvas.getByRole('button', { name: /sign in/i });
    
    // Simulate user interaction
    await userEvent.type(emailInput, 'user@example.com');
    await userEvent.type(passwordInput, 'password123');
    
    // Verify inputs
    await expect(emailInput).toHaveValue('user@example.com');
    await expect(passwordInput).toHaveValue('password123');
    
    // Submit form
    await userEvent.click(submitButton);
  }
};

// Error state
export const WithErrors: Story = {
  args: {
    errors: {
      email: 'Invalid email format',
      password: 'Password is required'
    }
  }
};

Example: MDX documentation with embedded stories

<!-- Button.stories.mdx -->
import { Canvas, Meta, Story, ArgTypes } from '@storybook/blocks';
import { Button } from './Button';
import * as ButtonStories from './Button.stories';

<Meta of={ButtonStories} />

# Button Component

A versatile button component with multiple variants and sizes.

## Usage

```tsx
import { Button } from '@/components/Button';

function App() {
  return (
    <Button variant="primary" onClick={() => alert('Clicked!')}>
      Click Me
    </Button>
  );
}
```

## Variants

<Canvas of={ButtonStories.Primary} />
<Canvas of={ButtonStories.Secondary} />
<Canvas of={ButtonStories.Danger} />

## Props

<ArgTypes of={Button} />

## Accessibility

- Keyboard navigable with Tab
- Supports aria-label and aria-describedby
- Disabled state properly communicated to screen readers

## Best Practices

- Use semantic button text (avoid "Click here")
- Provide loading state feedback
- Use appropriate variant for action importance
Storybook Benefits: Component catalog, visual testing with Chromatic, interaction testing, accessibility auditing, responsive design testing, and living documentation for design systems.

16.6 Bundle Analyzer Performance Monitoring

Analyze bundle size, identify large dependencies, and optimize JavaScript bundles for better performance.

Tool Platform Features Best For
webpack-bundle-analyzer Webpack Interactive treemap, module sizes Webpack projects
rollup-plugin-visualizer Vite/Rollup Multiple chart types, treemap Vite projects
source-map-explorer Any bundler Source map analysis Generic analysis
bundlephobia Web service npm package size lookup Dependency evaluation

Example: Webpack Bundle Analyzer setup

// Installation
npm install -D webpack-bundle-analyzer

// webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: process.env.ANALYZE ? 'server' : 'disabled',
      openAnalyzer: true,
      generateStatsFile: true,
      statsFilename: 'bundle-stats.json'
    })
  ]
};

// package.json
{
  "scripts": {
    "build": "webpack --mode production",
    "build:analyze": "ANALYZE=true webpack --mode production"
  }
}

// Run analysis
npm run build:analyze

// Create React App
npm install -D webpack-bundle-analyzer
npm run build
npx webpack-bundle-analyzer build/static/js/*.js

Example: Vite bundle analysis with visualizer

// Installation
npm install -D rollup-plugin-visualizer

// vite.config.ts
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    visualizer({
      filename: './dist/stats.html',
      open: true,
      gzipSize: true,
      brotliSize: true,
      template: 'treemap' // or 'sunburst', 'network'
    })
  ],
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor-react': ['react', 'react-dom'],
          'vendor-ui': ['@mui/material', '@emotion/react'],
          'vendor-utils': ['lodash-es', 'date-fns']
        }
      }
    }
  }
});

// package.json
{
  "scripts": {
    "build": "vite build",
    "build:analyze": "vite build --mode production"
  }
}

// Output: dist/stats.html with interactive visualization

Example: Next.js bundle analysis

// Installation
npm install -D @next/bundle-analyzer

// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true'
});

module.exports = withBundleAnalyzer({
  // Next.js config
  reactStrictMode: true,
  swcMinify: true,
  
  // Optimize bundle
  webpack: (config, { isServer }) => {
    if (!isServer) {
      // Client-side optimizations
      config.optimization.splitChunks = {
        chunks: 'all',
        cacheGroups: {
          default: false,
          vendors: false,
          // Vendor chunk
          vendor: {
            name: 'vendor',
            chunks: 'all',
            test: /node_modules/
          },
          // Common chunks
          common: {
            name: 'common',
            minChunks: 2,
            chunks: 'all',
            priority: 10,
            reuseExistingChunk: true,
            enforce: true
          }
        }
      };
    }
    return config;
  }
});

// package.json
{
  "scripts": {
    "build": "next build",
    "analyze": "ANALYZE=true next build"
  }
}

// Run: npm run analyze
// Opens two browser tabs: client and server bundles

Example: Bundle size budgets and monitoring

// webpack.config.js - Size budgets
module.exports = {
  performance: {
    maxAssetSize: 250000, // 250kb
    maxEntrypointSize: 400000, // 400kb
    hints: 'error', // or 'warning'
    assetFilter: (assetFilename) => {
      return assetFilename.endsWith('.js');
    }
  }
};

// vite.config.ts - Size warnings
export default defineConfig({
  build: {
    chunkSizeWarningLimit: 500, // 500kb warning threshold
    rollupOptions: {
      output: {
        manualChunks(id) {
          // Split large dependencies
          if (id.includes('node_modules')) {
            if (id.includes('react')) return 'vendor-react';
            if (id.includes('@mui')) return 'vendor-ui';
            if (id.includes('lodash')) return 'vendor-utils';
            return 'vendor';
          }
        }
      }
    }
  }
});

// package.json - Bundle size tracking
{
  "scripts": {
    "size": "size-limit",
    "size:why": "size-limit --why"
  },
  "size-limit": [
    {
      "path": "dist/index.js",
      "limit": "200 KB"
    },
    {
      "path": "dist/vendor.js",
      "limit": "300 KB"
    }
  ]
}

// .github/workflows/size.yml - CI size check
name: Size Check
on: pull_request

jobs:
  size:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: andresz1/size-limit-action@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

Development Workflow Optimization Checklist

17. Advanced Frontend Architecture Patterns

17.1 Micro-Frontend Module Federation

Split large applications into independently deployable micro-frontends that can be developed, tested, and deployed by separate teams.

Approach Technology Benefits Challenges
Module Federation Webpack 5 Runtime integration, shared dependencies Complex configuration
Iframe Approach Native iframe Complete isolation, simple Poor performance, communication overhead
Web Components Custom Elements Framework agnostic, standards-based Styling isolation issues
Single-SPA Single-SPA framework Framework agnostic orchestration Learning curve, complexity

Example: Webpack Module Federation setup

// Host app (shell) - webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        // Load remote apps
        app1: 'app1@http://localhost:3001/remoteEntry.js',
        app2: 'app2@http://localhost:3002/remoteEntry.js'
      },
      shared: {
        // Share dependencies
        react: { singleton: true, requiredVersion: '^18.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
      }
    })
  ]
};

// Remote app 1 - webpack.config.js
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'app1',
      filename: 'remoteEntry.js',
      exposes: {
        // Expose components
        './Button': './src/components/Button',
        './Dashboard': './src/pages/Dashboard'
      },
      shared: {
        react: { singleton: true },
        'react-dom': { singleton: true }
      }
    })
  ]
};

// Host app - Load remote component
import React, { lazy, Suspense } from 'react';

const RemoteDashboard = lazy(() => import('app1/Dashboard'));
const RemoteButton = lazy(() => import('app1/Button'));

function App() {
  return (
    <div>
      <h1>Host Application</h1>
      <Suspense fallback="Loading Dashboard...">
        <RemoteDashboard />
      </Suspense>
      <Suspense fallback="Loading Button...">
        <RemoteButton />
      </Suspense>
    </div>
  );
}

Example: Vite Module Federation with plugin

// Installation
npm install -D @originjs/vite-plugin-federation

// Host app - vite.config.ts
import { defineConfig } from 'vite';
import federation from '@originjs/vite-plugin-federation';

export default defineConfig({
  plugins: [
    federation({
      name: 'host-app',
      remotes: {
        remoteApp: 'http://localhost:3001/assets/remoteEntry.js'
      },
      shared: ['react', 'react-dom']
    })
  ],
  build: {
    target: 'esnext',
    minify: false
  }
});

// Remote app - vite.config.ts
export default defineConfig({
  plugins: [
    federation({
      name: 'remote-app',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/components/Button',
        './Header': './src/components/Header'
      },
      shared: ['react', 'react-dom']
    })
  ],
  build: {
    target: 'esnext'
  }
});

// Host usage
import { lazy } from 'react';

const RemoteButton = lazy(() => import('remoteApp/Button'));

function App() {
  return <RemoteButton />;
}

Example: Communication between micro-frontends

// Shared event bus for communication
// eventBus.ts
class EventBus {
  private events: Map<string, Function[]> = new Map();
  
  subscribe(event: string, callback: Function) {
    if (!this.events.has(event)) {
      this.events.set(event, []);
    }
    this.events.get(event)!.push(callback);
    
    // Return unsubscribe function
    return () => {
      const callbacks = this.events.get(event);
      if (callbacks) {
        const index = callbacks.indexOf(callback);
        if (index > -1) callbacks.splice(index, 1);
      }
    };
  }
  
  publish(event: string, data?: any) {
    const callbacks = this.events.get(event);
    if (callbacks) {
      callbacks.forEach(cb => cb(data));
    }
  }
}

export const eventBus = new EventBus();

// App 1 - Publish event
import { eventBus } from '@shared/eventBus';

function CartComponent() {
  const addToCart = (item: Product) => {
    eventBus.publish('cart:add', { item, quantity: 1 });
  };
  
  return <button onClick={() => addToCart(product)}>Add to Cart</button>;
}

// App 2 - Subscribe to event
import { eventBus } from '@shared/eventBus';
import { useEffect, useState } from 'react';

function CartBadge() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    const unsubscribe = eventBus.subscribe('cart:add', (data) => {
      setCount(prev => prev + data.quantity);
    });
    
    return unsubscribe;
  }, []);
  
  return <div>Cart: {count}</div>;
}

// Alternative: Use CustomEvents for cross-app communication
window.dispatchEvent(new CustomEvent('cart:update', { 
  detail: { items: 5 } 
}));

window.addEventListener('cart:update', (e) => {
  console.log('Cart updated:', e.detail);
});
Challenges: Version conflicts in shared dependencies, deployment coordination, testing complexity, increased bundle size. Use only for large-scale apps with multiple teams. Consider monorepo first.

17.2 Web Components Custom Elements

Create reusable, framework-agnostic components using Web Components standards (Custom Elements, Shadow DOM, HTML Templates).

API Purpose Use Case Browser Support
Custom Elements Define custom HTML tags Reusable components Excellent
Shadow DOM Encapsulated styles and DOM Style isolation Excellent
HTML Templates Reusable DOM fragments Component templates Excellent
Slots Content projection Flexible layouts Excellent

Example: Creating Custom Element with Shadow DOM

// my-button.ts - Custom button component
class MyButton extends HTMLElement {
  private _disabled: boolean = false;
  
  constructor() {
    super();
    // Create Shadow DOM for style encapsulation
    this.attachShadow({ mode: 'open' });
  }
  
  // Observed attributes - trigger attributeChangedCallback
  static get observedAttributes() {
    return ['disabled', 'variant'];
  }
  
  // Lifecycle: Element added to DOM
  connectedCallback() {
    this.render();
    this.addEventListener('click', this.handleClick);
  }
  
  // Lifecycle: Element removed from DOM
  disconnectedCallback() {
    this.removeEventListener('click', this.handleClick);
  }
  
  // Lifecycle: Attribute changed
  attributeChangedCallback(name: string, oldValue: string, newValue: string) {
    if (name === 'disabled') {
      this._disabled = newValue !== null;
    }
    this.render();
  }
  
  handleClick = (e: Event) => {
    if (this._disabled) {
      e.preventDefault();
      e.stopPropagation();
      return;
    }
    // Dispatch custom event
    this.dispatchEvent(new CustomEvent('button-click', {
      bubbles: true,
      composed: true,
      detail: { timestamp: Date.now() }
    }));
  };
  
  render() {
    const variant = this.getAttribute('variant') || 'primary';
    
    this.shadowRoot!.innerHTML = `
      <style>
        :host {
          display: inline-block;
        }
        
        button {
          padding: 12px 24px;
          border: none;
          border-radius: 4px;
          font-size: 16px;
          cursor: pointer;
          transition: all 0.2s;
        }
        
        button.primary {
          background: #007acc;
          color: white;
        }
        
        button.secondary {
          background: #6c757d;
          color: white;
        }
        
        button:hover:not(:disabled) {
          opacity: 0.9;
          transform: translateY(-1px);
        }
        
        button:disabled {
          opacity: 0.5;
          cursor: not-allowed;
        }
      </style>
      <button class="${variant}" ?disabled="${this._disabled}">
        <slot></slot>
      </button>
    `;
  }
}

// Register custom element
customElements.define('my-button', MyButton);

// Usage in HTML
<my-button variant="primary">Click Me</my-button>
<my-button variant="secondary" disabled>Disabled</my-button>

// Usage in JavaScript
const button = document.createElement('my-button');
button.setAttribute('variant', 'primary');
button.textContent = 'Dynamic Button';
button.addEventListener('button-click', (e) => {
  console.log('Clicked at:', e.detail.timestamp);
});
document.body.appendChild(button);

Example: Web Component with Lit library (simplifies creation)

// Installation
npm install lit

// my-card.ts
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('my-card')
export class MyCard extends LitElement {
  @property({ type: String }) title = '';
  @property({ type: String }) subtitle = '';
  @property({ type: Boolean }) loading = false;
  
  // Scoped styles
  static styles = css`
    :host {
      display: block;
      border: 1px solid #ddd;
      border-radius: 8px;
      padding: 16px;
      background: white;
    }
    
    .title {
      font-size: 20px;
      font-weight: bold;
      margin-bottom: 8px;
    }
    
    .subtitle {
      color: #666;
      font-size: 14px;
    }
    
    .loading {
      opacity: 0.6;
    }
  `;
  
  render() {
    return html`
      <div class="${this.loading ? 'loading' : ''}">
        <div class="title">${this.title}</div>
        <div class="subtitle">${this.subtitle}</div>
        <slot></slot>
      </div>
    `;
  }
}

// Usage
<my-card title="Card Title" subtitle="Card subtitle">
  <p>Card content goes here</p>
</my-card>

// React integration
function App() {
  return (
    <my-card 
      title="From React" 
      subtitle="Web Component in React"
    >
      <p>Content</p>
    </my-card>
  );
}

Example: Slots for content projection

// dialog-box.ts
class DialogBox extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }
  
  connectedCallback() {
    this.shadowRoot!.innerHTML = `
      <style>
        .dialog {
          border: 1px solid #ccc;
          border-radius: 8px;
          padding: 20px;
          background: white;
          box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }
        
        .header {
          font-size: 20px;
          font-weight: bold;
          margin-bottom: 12px;
        }
        
        .content {
          margin-bottom: 16px;
        }
        
        .footer {
          display: flex;
          justify-content: flex-end;
          gap: 8px;
        }
      </style>
      
      <div class="dialog">
        <div class="header">
          <slot name="header">Default Header</slot>
        </div>
        <div class="content">
          <slot>Default content</slot>
        </div>
        <div class="footer">
          <slot name="footer"></slot>
        </div>
      </div>
    `;
  }
}

customElements.define('dialog-box', DialogBox);

// Usage with named slots
<dialog-box>
  <span slot="header">Confirm Action</span>
  <p>Are you sure you want to delete this item?</p>
  <div slot="footer">
    <button>Cancel</button>
    <button>Confirm</button>
  </div>
</dialog-box>
When to Use: Design systems shared across multiple frameworks, embeddable widgets, library components. Lit, Stencil, or vanilla Web Components all work. Full framework agnosticism with standards-based approach.

17.3 Island Architecture Astro Qwik

Ship zero JavaScript by default, hydrate interactive components only when needed for optimal performance.

Framework Approach Hydration Strategy Best For
Astro Islands Architecture Partial, on-demand Content sites, blogs, docs
Qwik Resumability No hydration, resume on interaction Web apps, e-commerce
Next.js Islands Server Components Selective client components Full-stack apps
Marko Automatic partial hydration Component-level eBay's solution

Example: Astro Islands Architecture

// Installation
npm create astro@latest

// src/pages/index.astro
---
import Layout from '../layouts/Layout.astro';
import Counter from '../components/Counter.jsx'; // React component
import Carousel from '../components/Carousel.vue'; // Vue component
---

<Layout title="Astro Islands">
  <h1>Welcome to Astro</h1>
  
  <!-- Static content - no JS shipped -->
  <p>This content is completely static.</p>
  
  <!-- Island 1: Load immediately -->
  <Counter client:load />
  
  <!-- Island 2: Load when visible -->
  <Carousel client:visible />
  
  <!-- Island 3: Load on idle -->
  <Comments client:idle />
  
  <!-- Island 4: Load on media query -->
  <MobileMenu client:media="(max-width: 768px)" />
  
  <!-- No client directive = zero JS -->
  <StaticComponent />
</Layout>

// Hydration directives:
// client:load - Load immediately
// client:idle - Load when browser is idle
// client:visible - Load when scrolled into view
// client:media - Load based on media query
// client:only - Only run on client (skip SSR)

// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import vue from '@astrojs/vue';
import svelte from '@astrojs/svelte';

export default defineConfig({
  integrations: [react(), vue(), svelte()],
  output: 'static' // or 'server' for SSR
});

Example: Qwik resumability (no hydration)

// Installation
npm create qwik@latest

// src/routes/index.tsx
import { component$, useSignal } from '@builder.io/qwik';

export default component$(() => {
  const count = useSignal(0);
  
  return (
    <div>
      <h1>Qwik Counter</h1>
      <p>Count: {count.value}</p>
      
      {/* Event handlers are lazy-loaded on interaction */}
      <button onClick$={() => count.value++}>
        Increment
      </button>
    </div>
  );
});

// Key concepts:
// 1. $ suffix - Qwik optimizer boundary
// 2. useSignal - Reactive state
// 3. No hydration - app "resumes" from server state
// 4. Lazy load event handlers only when needed

// src/components/product-list.tsx
import { component$, Resource, useResource$ } from '@builder.io/qwik';

export default component$(() => {
  // Data fetching with suspense
  const products = useResource$(async () => {
    const res = await fetch('/api/products');
    return res.json();
  });
  
  return (
    <Resource
      value={products}
      onPending={() => <div>Loading...</div>}
      onRejected={(error) => <div>Error: {error.message}</div>}
      onResolved={(data) => (
        <ul>
          {data.map((product) => (
            <li key={product.id}>{product.name}</li>
          ))}
        </ul>
      )}
    />
  );
});

// Performance benefits:
// - Instant Time to Interactive (TTI)
// - Progressive interactivity
// - Optimal Core Web Vitals

Example: Astro with React islands for interactivity

// src/components/InteractiveCart.jsx (React)
import { useState } from 'react';

export default function InteractiveCart({ initialItems = [] }) {
  const [items, setItems] = useState(initialItems);
  
  const addItem = (item) => {
    setItems([...items, item]);
  };
  
  const removeItem = (id) => {
    setItems(items.filter(item => item.id !== id));
  };
  
  return (
    <div className="cart">
      <h2>Cart ({items.length})</h2>
      <ul>
        {items.map(item => (
          <li key={item.id}>
            {item.name} - ${item.price}
            <button onClick={() => removeItem(item.id)}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

// src/pages/shop.astro
---
import Layout from '../layouts/Layout.astro';
import InteractiveCart from '../components/InteractiveCart.jsx';
import ProductCard from '../components/ProductCard.astro';

const products = await fetch('https://api.example.com/products').then(r => r.json());
---

<Layout title="Shop">
  <!-- Static product grid (no JS) -->
  <div class="products">
    {products.map(product => (
      <ProductCard product={product} />
    ))}
  </div>
  
  <!-- Interactive cart (JS island, loads when visible) -->
  <InteractiveCart client:visible initialItems={[]} />
</Layout>

// Result: Only cart component ships JavaScript
// Products are pure HTML/CSS = instant rendering
Performance Impact: Islands reduce JavaScript by 90%+. Perfect for content-heavy sites. Use Astro for static sites with islands of interactivity. Use Qwik for full web apps with optimal performance.

17.4 Server Components Next.js 13

React Server Components render on server, reducing client bundle size and enabling direct backend access without APIs.

Component Type Rendering Capabilities Use Case
Server Component Server only Direct DB access, no client JS Static content, data fetching
Client Component Client + Server Hooks, events, browser APIs Interactive UI
Shared Component Both (default) Limited to serializable props Reusable components

Example: Next.js App Router with Server Components

// app/page.tsx - Server Component (default)
import { db } from '@/lib/database';
import ProductList from './ProductList';

// This runs ONLY on the server
export default async function HomePage() {
  // Direct database access (no API route needed)
  const products = await db.product.findMany({
    include: { category: true }
  });
  
  // Can use Node.js APIs
  const fs = require('fs');
  const data = fs.readFileSync('./data.json', 'utf8');
  
  return (
    <div>
      <h1>Products</h1>
      <ProductList products={products} />
    </div>
  );
}

// No JavaScript shipped for this component!

// app/ProductList.tsx - Client Component
'use client'; // Directive to mark as client component

import { useState } from 'react';

export default function ProductList({ products }) {
  const [filter, setFilter] = useState('');
  
  const filtered = products.filter(p => 
    p.name.toLowerCase().includes(filter.toLowerCase())
  );
  
  return (
    <div>
      <input 
        type="text"
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        placeholder="Filter products..."
      />
      <ul>
        {filtered.map(product => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  );
}

// This ships JavaScript for interactivity

Example: Data fetching patterns with Server Components

// app/dashboard/page.tsx
import { Suspense } from 'react';
import UserStats from './UserStats';
import RecentOrders from './RecentOrders';
import Analytics from './Analytics';

export default function DashboardPage() {
  return (
    <div>
      <h1>Dashboard</h1>
      
      {/* Stream components independently */}
      <Suspense fallback={<div>Loading stats...</div>}>
        <UserStats />
      </Suspense>
      
      <Suspense fallback={<div>Loading orders...</div>}>
        <RecentOrders />
      </Suspense>
      
      <Suspense fallback={<div>Loading analytics...</div>}>
        <Analytics />
      </Suspense>
    </div>
  );
}

// app/dashboard/UserStats.tsx - Server Component
async function UserStats() {
  // Each component fetches its own data
  const stats = await fetch('https://api.example.com/stats', {
    // Next.js extended fetch with caching
    next: { revalidate: 60 } // Revalidate every 60 seconds
  }).then(r => r.json());
  
  return (
    <div className="stats">
      <div>Users: {stats.users}</div>
      <div>Active: {stats.active}</div>
    </div>
  );
}

// app/dashboard/RecentOrders.tsx
async function RecentOrders() {
  const orders = await db.order.findMany({
    take: 10,
    orderBy: { createdAt: 'desc' }
  });
  
  return (
    <ul>
      {orders.map(order => (
        <li key={order.id}>
          Order #{order.id} - ${order.total}
        </li>
      ))}
    </ul>
  );
}

// Parallel data fetching with streaming
// Each Suspense boundary resolves independently

Example: Mixing Server and Client Components

// app/product/[id]/page.tsx - Server Component
import { db } from '@/lib/database';
import AddToCartButton from './AddToCartButton';
import RelatedProducts from './RelatedProducts';

export default async function ProductPage({ params }) {
  const product = await db.product.findUnique({
    where: { id: params.id },
    include: { reviews: true }
  });
  
  return (
    <div>
      {/* Server-rendered content */}
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <p>Price: ${product.price}</p>
      
      {/* Client component for interactivity */}
      <AddToCartButton productId={product.id} />
      
      {/* Server component for related products */}
      <RelatedProducts categoryId={product.categoryId} />
      
      {/* Server-rendered reviews */}
      <div>
        <h2>Reviews</h2>
        {product.reviews.map(review => (
          <div key={review.id}>
            <strong>{review.author}</strong>
            <p>{review.content}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

// app/product/[id]/AddToCartButton.tsx - Client Component
'use client';

import { useState } from 'react';

export default function AddToCartButton({ productId }) {
  const [loading, setLoading] = useState(false);
  
  const handleAdd = async () => {
    setLoading(true);
    await fetch('/api/cart', {
      method: 'POST',
      body: JSON.stringify({ productId })
    });
    setLoading(false);
  };
  
  return (
    <button onClick={handleAdd} disabled={loading}>
      {loading ? 'Adding...' : 'Add to Cart'}
    </button>
  );
}

// Rules:
// 1. Server components can import client components
// 2. Client components CANNOT import server components directly
// 3. Pass server components as children to client components
Limitations: Cannot use hooks, event handlers, or browser APIs in Server Components. Props must be serializable (no functions). Requires Next.js 13+ App Router. Learning curve for new mental model.

17.5 Edge Computing Vercel Functions

Deploy serverless functions to edge locations globally for ultra-low latency API responses and dynamic content.

Platform Runtime Cold Start Use Case
Vercel Edge Functions V8 isolates <50ms API routes, middleware
Cloudflare Workers V8 isolates <5ms Edge compute, KV storage
AWS Lambda@Edge Node.js ~100ms CloudFront customization
Netlify Edge Functions Deno <50ms JAMstack edge logic

Example: Vercel Edge Function (Next.js)

// app/api/edge/route.ts
import { NextRequest, NextResponse } from 'next/server';

export const runtime = 'edge'; // Enable Edge Runtime

export async function GET(request: NextRequest) {
  // Access geo location
  const country = request.geo?.country || 'Unknown';
  const city = request.geo?.city || 'Unknown';
  
  // Fast external API call (co-located at edge)
  const data = await fetch('https://api.example.com/data', {
    headers: { 'Authorization': process.env.API_KEY }
  }).then(r => r.json());
  
  return NextResponse.json({
    message: `Hello from ${city}, ${country}!`,
    data,
    edge: true
  });
}

// Edge middleware - runs before every request
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // Geo-based redirect
  const country = request.geo?.country;
  
  if (country === 'CN' && !request.nextUrl.pathname.startsWith('/cn')) {
    return NextResponse.redirect(new URL('/cn', request.url));
  }
  
  // A/B testing
  const bucket = Math.random() < 0.5 ? 'a' : 'b';
  const response = NextResponse.next();
  response.cookies.set('ab-test', bucket);
  
  // Add custom headers
  response.headers.set('x-edge-location', request.geo?.city || 'Unknown');
  
  return response;
}

export const config = {
  matcher: ['/api/:path*', '/products/:path*']
};

Example: Cloudflare Workers with KV storage

// worker.js
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const url = new URL(request.url);
  
  // Check cache first
  const cache = caches.default;
  let response = await cache.match(request);
  
  if (response) {
    return response;
  }
  
  // Access KV storage (ultra-fast key-value store)
  const config = await MY_KV_NAMESPACE.get('config', { type: 'json' });
  
  // Generate response
  response = new Response(JSON.stringify({
    message: 'Hello from Cloudflare Edge',
    config,
    location: request.cf?.city
  }), {
    headers: {
      'content-type': 'application/json',
      'cache-control': 'public, max-age=60'
    }
  });
  
  // Cache response
  event.waitUntil(cache.put(request, response.clone()));
  
  return response;
}

// wrangler.toml
name = "my-worker"
main = "worker.js"
compatibility_date = "2024-01-01"

[[kv_namespaces]]
binding = "MY_KV_NAMESPACE"
id = "your-namespace-id"

// Deploy: npx wrangler deploy

Example: Edge function for personalization

// app/api/personalize/route.ts
import { NextRequest, NextResponse } from 'next/server';

export const runtime = 'edge';

export async function GET(request: NextRequest) {
  // Get user preferences from cookie
  const preferences = request.cookies.get('preferences')?.value;
  const parsed = preferences ? JSON.parse(preferences) : {};
  
  // Geo-based content
  const country = request.geo?.country || 'US';
  const currency = getCurrency(country);
  
  // A/B test variant
  const variant = request.cookies.get('ab-variant')?.value || 'control';
  
  // Time-based personalization
  const hour = new Date().getHours();
  const greeting = hour < 12 ? 'Good morning' : 
                   hour < 18 ? 'Good afternoon' : 'Good evening';
  
  return NextResponse.json({
    greeting,
    currency,
    variant,
    preferences: parsed,
    location: {
      country,
      city: request.geo?.city
    }
  });
}

function getCurrency(country: string): string {
  const currencyMap: Record<string, string> = {
    US: 'USD',
    GB: 'GBP',
    JP: 'JPY',
    DE: 'EUR'
  };
  return currencyMap[country] || 'USD';
}

// Usage in page
async function ProductPrice({ productId }) {
  const personalization = await fetch('/api/personalize').then(r => r.json());
  const price = await getPrice(productId, personalization.currency);
  
  return <div>{price}</div>;
}
Edge Benefits: 50-200ms faster than traditional serverless, global distribution, lower costs at scale. Limitations: No Node.js APIs, limited CPU time (50ms), smaller bundle size. Perfect for API routes, middleware, auth.

17.6 WebAssembly WASM Integration

Run high-performance compiled code (C, C++, Rust) in the browser at near-native speed for compute-intensive tasks.

Language Toolchain Use Case Performance
Rust wasm-pack, wasm-bindgen CPU-intensive algorithms Near-native
C/C++ Emscripten Porting existing libraries Near-native
AssemblyScript asc (TypeScript-like) TypeScript developers Very fast
Go TinyGo Small WASM binaries Fast

Example: Rust WebAssembly module with wasm-pack

// Installation
cargo install wasm-pack

// Create new Rust project
cargo new --lib image-processor
cd image-processor

// Cargo.toml
[package]
name = "image-processor"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"

// src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u32 {
    match n {
        0 => 0,
        1 => 1,
        _ => fibonacci(n - 1) + fibonacci(n - 2)
    }
}

#[wasm_bindgen]
pub struct ImageProcessor {
    width: u32,
    height: u32,
}

#[wasm_bindgen]
impl ImageProcessor {
    #[wasm_bindgen(constructor)]
    pub fn new(width: u32, height: u32) -> ImageProcessor {
        ImageProcessor { width, height }
    }
    
    pub fn grayscale(&self, pixels: &mut [u8]) {
        for chunk in pixels.chunks_mut(4) {
            let avg = (chunk[0] as u32 + chunk[1] as u32 + chunk[2] as u32) / 3;
            chunk[0] = avg as u8;
            chunk[1] = avg as u8;
            chunk[2] = avg as u8;
        }
    }
    
    pub fn blur(&self, pixels: &mut [u8], radius: u32) {
        // High-performance blur algorithm
        // ... implementation
    }
}

// Build WASM
// wasm-pack build --target web

// JavaScript usage
// src/App.tsx
import init, { fibonacci, ImageProcessor } from './wasm/image_processor';

async function App() {
  // Initialize WASM module
  await init();
  
  // Use Rust functions
  const result = fibonacci(10);
  console.log('Fibonacci(10):', result);
  
  // Process image
  const processor = new ImageProcessor(800, 600);
  const imageData = ctx.getImageData(0, 0, 800, 600);
  processor.grayscale(imageData.data);
  ctx.putImageData(imageData, 0, 0);
  
  return <div>WASM Loaded</div>;
}

Example: AssemblyScript for TypeScript developers

// Installation
npm install -D assemblyscript

// assembly/index.ts (AssemblyScript - TypeScript-like)
export function add(a: i32, b: i32): i32 {
  return a + b;
}

export function sortArray(arr: Int32Array): void {
  // Efficient sorting in WASM
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] > arr[j]) {
        const temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
      }
    }
  }
}

export function processMatrix(
  matrix: Float64Array,
  rows: i32,
  cols: i32
): Float64Array {
  const result = new Float64Array(rows * cols);
  
  for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
      const idx = i * cols + j;
      result[idx] = matrix[idx] * 2.0;
    }
  }
  
  return result;
}

// Build: npm run asbuild

// JavaScript usage
import { add, sortArray } from './build/release.js';

const result = add(5, 10);
console.log(result); // 15

const arr = new Int32Array([5, 2, 8, 1, 9]);
sortArray(arr);
console.log(arr); // [1, 2, 5, 8, 9]

Example: Real-world WASM use case - Image compression

// imageCompressor.ts - React component using WASM
import { useEffect, useState } from 'react';
import init, { compress_image } from './wasm/image_compressor';

export function ImageCompressor() {
  const [wasmReady, setWasmReady] = useState(false);
  
  useEffect(() => {
    init().then(() => setWasmReady(true));
  }, []);
  
  const handleCompress = async (file: File) => {
    if (!wasmReady) return;
    
    const arrayBuffer = await file.arrayBuffer();
    const uint8Array = new Uint8Array(arrayBuffer);
    
    // Call Rust WASM function
    const compressed = compress_image(uint8Array, 80); // 80% quality
    
    // Create download link
    const blob = new Blob([compressed], { type: 'image/jpeg' });
    const url = URL.createObjectURL(blob);
    
    const a = document.createElement('a');
    a.href = url;
    a.download = 'compressed.jpg';
    a.click();
  };
  
  return (
    <div>
      {wasmReady ? (
        <input 
          type="file" 
          accept="image/*" 
          onChange={(e) => handleCompress(e.target.files[0])}
        />
      ) : (
        <p>Loading WASM module...</p>
      )}
    </div>
  );
}

// Performance comparison:
// JavaScript image compression: ~2000ms
// WASM image compression: ~200ms
// 10x faster!

Advanced Architecture Decision Matrix

Pattern When to Use Benefits Complexity
Micro-Frontends Large orgs, multiple teams, gradual migration Independent deployment, tech diversity High
Web Components Design systems, framework-agnostic libraries Standards-based, reusable everywhere Medium
Islands Architecture Content sites with selective interactivity Minimal JS, excellent performance Low-Medium
Server Components Data-heavy apps, reducing client bundle Zero client JS, direct backend access Medium
Edge Computing Global apps, low-latency requirements Ultra-fast responses, geo-distribution Low-Medium
WebAssembly CPU-intensive tasks, porting existing code Near-native performance High

18. Progressive Web App Implementation

18.1 Service Worker Workbox Caching

Implement service workers with Google Workbox for offline functionality, caching strategies, and background resource management.

Strategy Use Case Behavior Fallback
Network First API calls, dynamic content Try network, fallback to cache Stale data on offline
Cache First Static assets (CSS, JS, images) Serve from cache, update in background Fast but potentially stale
Stale While Revalidate Frequently updated content Serve cache, fetch fresh copy Balance speed & freshness
Network Only Real-time data, auth requests Always fetch from network Fail if offline
Cache Only Pre-cached app shell Only serve from cache Fast, no network needed

Example: Workbox service worker setup

// Installation
npm install workbox-webpack-plugin --save-dev

// webpack.config.js
const WorkboxPlugin = require('workbox-webpack-plugin');

module.exports = {
  plugins: [
    new WorkboxPlugin.GenerateSW({
      clientsClaim: true,
      skipWaiting: true,
      runtimeCaching: [
        {
          urlPattern: /^https:\/\/api\.example\.com/,
          handler: 'NetworkFirst',
          options: {
            cacheName: 'api-cache',
            expiration: {
              maxEntries: 50,
              maxAgeSeconds: 5 * 60 // 5 minutes
            },
            networkTimeoutSeconds: 10
          }
        },
        {
          urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/,
          handler: 'CacheFirst',
          options: {
            cacheName: 'image-cache',
            expiration: {
              maxEntries: 60,
              maxAgeSeconds: 30 * 24 * 60 * 60 // 30 days
            }
          }
        },
        {
          urlPattern: /\.(?:js|css)$/,
          handler: 'StaleWhileRevalidate',
          options: {
            cacheName: 'static-resources'
          }
        }
      ]
    })
  ]
};

// Register service worker in app
// index.tsx
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker
      .register('/service-worker.js')
      .then(registration => {
        console.log('SW registered:', registration);
        
        // Check for updates
        registration.addEventListener('updatefound', () => {
          const newWorker = registration.installing;
          newWorker?.addEventListener('statechange', () => {
            if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
              // New version available
              showUpdateNotification();
            }
          });
        });
      })
      .catch(error => {
        console.error('SW registration failed:', error);
      });
  });
}

Example: Custom Workbox service worker

// service-worker.js
import { precacheAndRoute, cleanupOutdatedCaches } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { NetworkFirst, CacheFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
import { CacheableResponsePlugin } from 'workbox-cacheable-response';

// Clean up old caches
cleanupOutdatedCaches();

// Precache assets from build
precacheAndRoute(self.__WB_MANIFEST);

// Cache API responses with NetworkFirst
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst({
    cacheName: 'api-cache',
    plugins: [
      new CacheableResponsePlugin({
        statuses: [0, 200]
      }),
      new ExpirationPlugin({
        maxEntries: 50,
        maxAgeSeconds: 5 * 60,
        purgeOnQuotaError: true
      })
    ]
  })
);

// Cache images with CacheFirst
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'image-cache',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 100,
        maxAgeSeconds: 30 * 24 * 60 * 60
      })
    ]
  })
);

// Cache Google Fonts with StaleWhileRevalidate
registerRoute(
  ({ url }) => url.origin === 'https://fonts.googleapis.com',
  new StaleWhileRevalidate({
    cacheName: 'google-fonts-stylesheets'
  })
);

// Offline fallback page
const FALLBACK_HTML_URL = '/offline.html';
const CACHE_NAME = 'offline-fallbacks';

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.add(FALLBACK_HTML_URL))
  );
});

self.addEventListener('fetch', (event) => {
  if (event.request.mode === 'navigate') {
    event.respondWith(
      fetch(event.request).catch(() => {
        return caches.match(FALLBACK_HTML_URL);
      })
    );
  }
});

Example: React hook for service worker updates

// useServiceWorker.ts
import { useEffect, useState } from 'react';

export function useServiceWorker() {
  const [updateAvailable, setUpdateAvailable] = useState(false);
  const [registration, setRegistration] = useState<ServiceWorkerRegistration | null>(null);

  useEffect(() => {
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/service-worker.js').then((reg) => {
        setRegistration(reg);

        // Check for updates every hour
        setInterval(() => {
          reg.update();
        }, 60 * 60 * 1000);

        reg.addEventListener('updatefound', () => {
          const newWorker = reg.installing;
          newWorker?.addEventListener('statechange', () => {
            if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
              setUpdateAvailable(true);
            }
          });
        });
      });
    }
  }, []);

  const updateServiceWorker = () => {
    if (registration?.waiting) {
      registration.waiting.postMessage({ type: 'SKIP_WAITING' });
      window.location.reload();
    }
  };

  return { updateAvailable, updateServiceWorker };
}

// Usage in component
function App() {
  const { updateAvailable, updateServiceWorker } = useServiceWorker();

  return (
    <div>
      {updateAvailable && (
        <div className="update-banner">
          <p>A new version is available!</p>
          <button onClick={updateServiceWorker}>Update Now</button>
        </div>
      )}
      {/* App content */}
    </div>
  );
}
Best Practices: Use NetworkFirst for API calls, CacheFirst for static assets, StaleWhileRevalidate for frequently updated content. Always provide offline fallback pages. Test with DevTools offline mode.

18.2 Web App Manifest Installation

Configure Web App Manifest for installable PWA with app-like experience, custom icons, splash screens, and display modes.

Property Purpose Required Example
name Full app name Yes "My Progressive Web App"
short_name Home screen name Yes "My PWA"
icons App icons (various sizes) Yes 192x192, 512x512 PNG
start_url Launch URL Yes "/?source=pwa"
display Display mode Yes standalone, fullscreen
theme_color Browser UI color No "#007acc"
background_color Splash screen color No "#ffffff"
orientation Screen orientation No portrait, landscape

Example: Complete manifest.json configuration

// public/manifest.json
{
  "name": "My Progressive Web Application",
  "short_name": "My PWA",
  "description": "A modern progressive web app with offline support",
  "start_url": "/?source=pwa",
  "display": "standalone",
  "theme_color": "#007acc",
  "background_color": "#ffffff",
  "orientation": "portrait-primary",
  "icons": [
    {
      "src": "/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "any maskable"
    }
  ],
  "screenshots": [
    {
      "src": "/screenshots/desktop.png",
      "sizes": "1280x720",
      "type": "image/png",
      "form_factor": "wide"
    },
    {
      "src": "/screenshots/mobile.png",
      "sizes": "750x1334",
      "type": "image/png",
      "form_factor": "narrow"
    }
  ],
  "categories": ["productivity", "utilities"],
  "shortcuts": [
    {
      "name": "New Document",
      "short_name": "New",
      "description": "Create a new document",
      "url": "/new?source=pwa",
      "icons": [{ "src": "/icons/new.png", "sizes": "96x96" }]
    },
    {
      "name": "Dashboard",
      "short_name": "Dashboard",
      "description": "View your dashboard",
      "url": "/dashboard?source=pwa",
      "icons": [{ "src": "/icons/dashboard.png", "sizes": "96x96" }]
    }
  ],
  "share_target": {
    "action": "/share",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "title": "title",
      "text": "text",
      "url": "url",
      "files": [
        {
          "name": "media",
          "accept": ["image/*", "video/*"]
        }
      ]
    }
  },
  "protocol_handlers": [
    {
      "protocol": "web+myapp",
      "url": "/handle?url=%s"
    }
  ]
}

// Add to HTML head
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#007acc">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="My PWA">
<link rel="apple-touch-icon" href="/icons/icon-192x192.png">

Example: Install prompt handling with React

// useInstallPrompt.ts
import { useEffect, useState } from 'react';

interface BeforeInstallPromptEvent extends Event {
  prompt: () => Promise<void>;
  userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
}

export function useInstallPrompt() {
  const [installPrompt, setInstallPrompt] = useState<BeforeInstallPromptEvent | null>(null);
  const [isInstalled, setIsInstalled] = useState(false);

  useEffect(() => {
    const handleBeforeInstall = (e: Event) => {
      e.preventDefault();
      setInstallPrompt(e as BeforeInstallPromptEvent);
    };

    const handleAppInstalled = () => {
      setIsInstalled(true);
      setInstallPrompt(null);
    };

    window.addEventListener('beforeinstallprompt', handleBeforeInstall);
    window.addEventListener('appinstalled', handleAppInstalled);

    // Check if already installed
    if (window.matchMedia('(display-mode: standalone)').matches) {
      setIsInstalled(true);
    }

    return () => {
      window.removeEventListener('beforeinstallprompt', handleBeforeInstall);
      window.removeEventListener('appinstalled', handleAppInstalled);
    };
  }, []);

  const promptInstall = async () => {
    if (!installPrompt) return false;

    await installPrompt.prompt();
    const { outcome } = await installPrompt.userChoice;

    if (outcome === 'accepted') {
      setInstallPrompt(null);
      return true;
    }

    return false;
  };

  return { canInstall: !!installPrompt, isInstalled, promptInstall };
}

// Usage in component
function InstallButton() {
  const { canInstall, isInstalled, promptInstall } = useInstallPrompt();

  if (isInstalled) {
    return <p>✓ App installed</p>;
  }

  if (!canInstall) {
    return null;
  }

  return (
    <button onClick={promptInstall}>
      📱 Install App
    </button>
  );
}

Example: Vite PWA plugin setup

// Installation
npm install -D vite-plugin-pwa

// vite.config.ts
import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';

export default defineConfig({
  plugins: [
    VitePWA({
      registerType: 'autoUpdate',
      includeAssets: ['favicon.ico', 'robots.txt', 'apple-touch-icon.png'],
      manifest: {
        name: 'My PWA App',
        short_name: 'PWA',
        description: 'Progressive Web Application',
        theme_color: '#007acc',
        icons: [
          {
            src: '/icon-192.png',
            sizes: '192x192',
            type: 'image/png'
          },
          {
            src: '/icon-512.png',
            sizes: '512x512',
            type: 'image/png'
          }
        ]
      },
      workbox: {
        runtimeCaching: [
          {
            urlPattern: /^https:\/\/api\./,
            handler: 'NetworkFirst',
            options: {
              cacheName: 'api-cache',
              expiration: {
                maxEntries: 50,
                maxAgeSeconds: 300
              }
            }
          }
        ]
      }
    })
  ]
});
Installation Requirements: HTTPS required (except localhost). Service worker must be registered. Manifest must include name, icons (192x192, 512x512), and start_url. User engagement needed before install prompt.

18.3 Push Notifications Web Push

Implement push notifications with Web Push API and service workers for re-engagement, updates, and real-time alerts.

Component Purpose Technology Required
VAPID Keys Server authentication Public/private key pair Yes
Push Subscription User permission & endpoint PushManager API Yes
Service Worker Receive notifications Push event listener Yes
Push Server Send notifications web-push library Yes
Notification Display message Notification API Yes

Example: Frontend push notification setup

// usePushNotifications.ts
import { useState, useEffect } from 'react';

const VAPID_PUBLIC_KEY = 'YOUR_PUBLIC_VAPID_KEY';

function urlBase64ToUint8Array(base64String: string): Uint8Array {
  const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
  const base64 = (base64String + padding)
    .replace(/\-/g, '+')
    .replace(/_/g, '/');
  const rawData = window.atob(base64);
  return Uint8Array.from([...rawData].map(char => char.charCodeAt(0)));
}

export function usePushNotifications() {
  const [subscription, setSubscription] = useState<PushSubscription | null>(null);
  const [permission, setPermission] = useState<NotificationPermission>('default');

  useEffect(() => {
    if ('Notification' in window) {
      setPermission(Notification.permission);
    }
  }, []);

  const subscribe = async () => {
    if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
      throw new Error('Push notifications not supported');
    }

    // Request permission
    const permission = await Notification.requestPermission();
    setPermission(permission);

    if (permission !== 'granted') {
      throw new Error('Permission denied');
    }

    // Get service worker registration
    const registration = await navigator.serviceWorker.ready;

    // Subscribe to push notifications
    const sub = await registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
    });

    setSubscription(sub);

    // Send subscription to backend
    await fetch('/api/push/subscribe', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(sub)
    });

    return sub;
  };

  const unsubscribe = async () => {
    if (!subscription) return;

    await subscription.unsubscribe();
    
    // Notify backend
    await fetch('/api/push/unsubscribe', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ endpoint: subscription.endpoint })
    });

    setSubscription(null);
  };

  return { subscription, permission, subscribe, unsubscribe };
}

// Usage in component
function NotificationSettings() {
  const { permission, subscribe, unsubscribe, subscription } = usePushNotifications();

  const handleEnable = async () => {
    try {
      await subscribe();
      alert('Notifications enabled!');
    } catch (error) {
      console.error('Failed to subscribe:', error);
    }
  };

  if (permission === 'denied') {
    return <p>Notifications blocked. Enable in browser settings.</p>;
  }

  return (
    <div>
      {subscription ? (
        <button onClick={unsubscribe}>Disable Notifications</button>
      ) : (
        <button onClick={handleEnable}>Enable Notifications</button>
      )}
    </div>
  );
}

Example: Service worker push event handler

// service-worker.js
self.addEventListener('push', (event) => {
  if (!event.data) return;

  const data = event.data.json();
  const { title, body, icon, badge, image, tag, actions, url } = data;

  const options = {
    body,
    icon: icon || '/icons/icon-192x192.png',
    badge: badge || '/icons/badge-72x72.png',
    image,
    tag: tag || 'default-tag',
    requireInteraction: false,
    vibrate: [200, 100, 200],
    data: { url: url || '/' },
    actions: actions || [
      { action: 'open', title: 'Open', icon: '/icons/open.png' },
      { action: 'close', title: 'Close', icon: '/icons/close.png' }
    ]
  };

  event.waitUntil(
    self.registration.showNotification(title, options)
  );
});

// Handle notification click
self.addEventListener('notificationclick', (event) => {
  event.notification.close();

  if (event.action === 'close') {
    return;
  }

  const url = event.notification.data?.url || '/';

  event.waitUntil(
    clients.matchAll({ type: 'window', includeUncontrolled: true })
      .then((clientList) => {
        // Check if window is already open
        for (const client of clientList) {
          if (client.url === url && 'focus' in client) {
            return client.focus();
          }
        }
        // Open new window
        if (clients.openWindow) {
          return clients.openWindow(url);
        }
      })
  );
});

// Handle notification close
self.addEventListener('notificationclose', (event) => {
  console.log('Notification closed:', event.notification.tag);
  
  // Track analytics
  fetch('/api/analytics/notification-close', {
    method: 'POST',
    body: JSON.stringify({ tag: event.notification.tag })
  });
});

Example: Backend push notification server (Node.js)

// Installation
npm install web-push

// Generate VAPID keys (run once)
const webpush = require('web-push');
const vapidKeys = webpush.generateVAPIDKeys();
console.log('Public Key:', vapidKeys.publicKey);
console.log('Private Key:', vapidKeys.privateKey);

// server.js
const express = require('express');
const webpush = require('web-push');

const app = express();
app.use(express.json());

// Configure VAPID keys
webpush.setVapidDetails(
  'mailto:your-email@example.com',
  process.env.VAPID_PUBLIC_KEY,
  process.env.VAPID_PRIVATE_KEY
);

// Store subscriptions (use database in production)
const subscriptions = new Map();

// Subscribe endpoint
app.post('/api/push/subscribe', (req, res) => {
  const subscription = req.body;
  subscriptions.set(subscription.endpoint, subscription);
  res.status(201).json({ success: true });
});

// Unsubscribe endpoint
app.post('/api/push/unsubscribe', (req, res) => {
  const { endpoint } = req.body;
  subscriptions.delete(endpoint);
  res.json({ success: true });
});

// Send notification
app.post('/api/push/send', async (req, res) => {
  const { title, body, url, userId } = req.body;

  const payload = JSON.stringify({
    title,
    body,
    icon: '/icons/icon-192x192.png',
    badge: '/icons/badge-72x72.png',
    url,
    actions: [
      { action: 'open', title: 'View' },
      { action: 'close', title: 'Dismiss' }
    ]
  });

  // Send to all subscriptions (or filter by userId)
  const promises = Array.from(subscriptions.values()).map(subscription =>
    webpush.sendNotification(subscription, payload)
      .catch(error => {
        // Handle expired subscriptions
        if (error.statusCode === 410) {
          subscriptions.delete(subscription.endpoint);
        }
        console.error('Push error:', error);
      })
  );

  await Promise.all(promises);
  res.json({ success: true, sent: promises.length });
});

app.listen(3000, () => console.log('Server running on port 3000'));
Best Practices: Always ask permission contextually, not immediately on page load. Provide clear value proposition. Allow easy unsubscribe. Use notification tags to avoid duplicates. Handle expired subscriptions gracefully.

18.4 Background Sync Offline Queue

Implement Background Sync API to defer actions until network connectivity is restored, ensuring reliable data submission.

Feature Purpose Use Case Browser Support
Background Sync Retry failed requests Form submissions, messages Chrome, Edge
Periodic Sync Regular background updates News, content refresh Limited support
SyncManager Register sync events Sync orchestration Chrome, Edge

Example: Background Sync for offline form submission

// useBackgroundSync.ts
import { useState } from 'react';

export function useBackgroundSync() {
  const [pending, setPending] = useState<number>(0);

  const queueSync = async (tag: string, data: any) => {
    if (!('serviceWorker' in navigator) || !('SyncManager' in window)) {
      // Fallback: immediate submission
      return fetch('/api/submit', {
        method: 'POST',
        body: JSON.stringify(data),
        headers: { 'Content-Type': 'application/json' }
      });
    }

    // Store data in IndexedDB
    const db = await openDatabase();
    await db.add('sync-queue', { tag, data, timestamp: Date.now() });

    // Register background sync
    const registration = await navigator.serviceWorker.ready;
    await registration.sync.register(tag);

    setPending(prev => prev + 1);
  };

  return { queueSync, pending };
}

// service-worker.js
self.addEventListener('sync', (event) => {
  if (event.tag.startsWith('form-submit-')) {
    event.waitUntil(syncFormData(event.tag));
  } else if (event.tag === 'message-queue') {
    event.waitUntil(syncMessages());
  }
});

async function syncFormData(tag) {
  const db = await openDatabase();
  const items = await db.getAllFromIndex('sync-queue', 'by-tag', tag);

  for (const item of items) {
    try {
      const response = await fetch('/api/submit', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(item.data)
      });

      if (response.ok) {
        await db.delete('sync-queue', item.id);
        
        // Notify clients
        const clients = await self.clients.matchAll();
        clients.forEach(client => {
          client.postMessage({
            type: 'SYNC_SUCCESS',
            tag: item.tag
          });
        });
      } else {
        throw new Error('Server error');
      }
    } catch (error) {
      console.error('Sync failed:', error);
      // Will retry automatically
    }
  }
}

async function syncMessages() {
  const db = await openDatabase();
  const messages = await db.getAll('message-queue');

  for (const message of messages) {
    try {
      await fetch('/api/messages', {
        method: 'POST',
        body: JSON.stringify(message),
        headers: { 'Content-Type': 'application/json' }
      });

      await db.delete('message-queue', message.id);
    } catch (error) {
      console.error('Message sync failed:', error);
    }
  }
}

Example: Offline-first form component

// OfflineForm.tsx
import { useState } from 'react';
import { useBackgroundSync } from './useBackgroundSync';

export function OfflineForm() {
  const [formData, setFormData] = useState({ name: '', email: '', message: '' });
  const [status, setStatus] = useState<'idle' | 'submitting' | 'queued'>('idle');
  const { queueSync } = useBackgroundSync();

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setStatus('submitting');

    try {
      if (navigator.onLine) {
        // Online: submit immediately
        const response = await fetch('/api/contact', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(formData)
        });

        if (response.ok) {
          alert('Form submitted successfully!');
          setFormData({ name: '', email: '', message: '' });
        }
      } else {
        // Offline: queue for background sync
        await queueSync('form-submit-contact', formData);
        setStatus('queued');
        alert('You are offline. Form will be submitted when connection is restored.');
      }
    } catch (error) {
      // Network error: queue for sync
      await queueSync('form-submit-contact', formData);
      setStatus('queued');
      alert('Submission queued. Will retry automatically.');
    } finally {
      setStatus('idle');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={formData.name}
        onChange={e => setFormData({ ...formData, name: e.target.value })}
        placeholder="Name"
        required
      />
      <input
        type="email"
        value={formData.email}
        onChange={e => setFormData({ ...formData, email: e.target.value })}
        placeholder="Email"
        required
      />
      <textarea
        value={formData.message}
        onChange={e => setFormData({ ...formData, message: e.target.value })}
        placeholder="Message"
        required
      />
      <button type="submit" disabled={status === 'submitting'}>
        {status === 'submitting' ? 'Submitting...' : 
         status === 'queued' ? 'Queued for Sync' : 
         'Submit'}
      </button>
    </form>
  );
}

Example: Periodic Background Sync (Chrome only)

// Register periodic sync (requires user engagement)
async function registerPeriodicSync() {
  const registration = await navigator.serviceWorker.ready;

  if ('periodicSync' in registration) {
    try {
      await registration.periodicSync.register('content-sync', {
        minInterval: 24 * 60 * 60 * 1000 // 24 hours
      });
      console.log('Periodic sync registered');
    } catch (error) {
      console.error('Periodic sync registration failed:', error);
    }
  }
}

// service-worker.js
self.addEventListener('periodicsync', (event) => {
  if (event.tag === 'content-sync') {
    event.waitUntil(syncContent());
  }
});

async function syncContent() {
  try {
    // Fetch latest content
    const response = await fetch('/api/content/latest');
    const content = await response.json();

    // Update cache
    const cache = await caches.open('content-cache');
    await cache.put('/api/content/latest', new Response(JSON.stringify(content)));

    // Notify clients
    const clients = await self.clients.matchAll();
    clients.forEach(client => {
      client.postMessage({
        type: 'CONTENT_UPDATED',
        count: content.length
      });
    });
  } catch (error) {
    console.error('Periodic sync failed:', error);
  }
}

// Unregister periodic sync
async function unregisterPeriodicSync() {
  const registration = await navigator.serviceWorker.ready;
  if ('periodicSync' in registration) {
    await registration.periodicSync.unregister('content-sync');
  }
}
Browser Support: Background Sync supported in Chrome, Edge, Opera. Safari and Firefox have limited/no support. Always implement fallback for immediate submission. Test offline scenarios thoroughly.

18.5 IndexedDB Storage Management

Use IndexedDB for client-side structured data storage with queries, indexes, and transactions for offline-first apps.

Library Features API Style Bundle Size
idb (Jake Archibald) Promise wrapper, simple API Modern async/await ~1KB
Dexie.js Rich queries, relationships Declarative ~20KB
LocalForage localStorage-like API Simple key-value ~8KB
PouchDB CouchDB sync, replication Document database ~140KB
// Installation
npm install idb

// db.ts - Database setup
import { openDB, DBSchema, IDBPDatabase } from 'idb';

interface MyDB extends DBSchema {
  products: {
    key: string;
    value: {
      id: string;
      name: string;
      price: number;
      category: string;
      createdAt: Date;
    };
    indexes: { 'by-category': string; 'by-price': number };
  };
  cart: {
    key: string;
    value: {
      productId: string;
      quantity: number;
      addedAt: Date;
    };
  };
  syncQueue: {
    key: number;
    value: {
      action: string;
      data: any;
      timestamp: number;
    };
  };
}

let dbPromise: Promise<IDBPDatabase<MyDB>> | null = null;

export function getDB() {
  if (!dbPromise) {
    dbPromise = openDB<MyDB>('my-pwa-db', 1, {
      upgrade(db) {
        // Create products store
        const productStore = db.createObjectStore('products', { keyPath: 'id' });
        productStore.createIndex('by-category', 'category');
        productStore.createIndex('by-price', 'price');

        // Create cart store
        db.createObjectStore('cart', { keyPath: 'productId' });

        // Create sync queue store
        db.createObjectStore('syncQueue', { keyPath: 'id', autoIncrement: true });
      }
    });
  }
  return dbPromise;
}

// products.ts - CRUD operations
export async function addProduct(product: MyDB['products']['value']) {
  const db = await getDB();
  await db.add('products', product);
}

export async function getProduct(id: string) {
  const db = await getDB();
  return db.get('products', id);
}

export async function getAllProducts() {
  const db = await getDB();
  return db.getAll('products');
}

export async function getProductsByCategory(category: string) {
  const db = await getDB();
  return db.getAllFromIndex('products', 'by-category', category);
}

export async function updateProduct(product: MyDB['products']['value']) {
  const db = await getDB();
  await db.put('products', product);
}

export async function deleteProduct(id: string) {
  const db = await getDB();
  await db.delete('products', id);
}

// Advanced queries with cursor
export async function getProductsInPriceRange(min: number, max: number) {
  const db = await getDB();
  const range = IDBKeyRange.bound(min, max);
  return db.getAllFromIndex('products', 'by-price', range);
}

// Bulk operations with transaction
export async function bulkAddProducts(products: MyDB['products']['value'][]) {
  const db = await getDB();
  const tx = db.transaction('products', 'readwrite');
  
  await Promise.all([
    ...products.map(product => tx.store.add(product)),
    tx.done
  ]);
}

Example: React hook for IndexedDB

// useIndexedDB.ts
import { useState, useEffect } from 'react';
import { getDB } from './db';
import type { DBSchema } from 'idb';

export function useIndexedDB<T>(storeName: string) {
  const [data, setData] = useState<T[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    loadData();
  }, [storeName]);

  const loadData = async () => {
    try {
      setLoading(true);
      const db = await getDB();
      const items = await db.getAll(storeName as any);
      setData(items as T[]);
      setError(null);
    } catch (err) {
      setError(err as Error);
    } finally {
      setLoading(false);
    }
  };

  const add = async (item: T) => {
    try {
      const db = await getDB();
      await db.add(storeName as any, item);
      await loadData();
    } catch (err) {
      setError(err as Error);
    }
  };

  const update = async (item: T) => {
    try {
      const db = await getDB();
      await db.put(storeName as any, item);
      await loadData();
    } catch (err) {
      setError(err as Error);
    }
  };

  const remove = async (key: string | number) => {
    try {
      const db = await getDB();
      await db.delete(storeName as any, key);
      await loadData();
    } catch (err) {
      setError(err as Error);
    }
  };

  const clear = async () => {
    try {
      const db = await getDB();
      await db.clear(storeName as any);
      setData([]);
    } catch (err) {
      setError(err as Error);
    }
  };

  return { data, loading, error, add, update, remove, clear, refresh: loadData };
}

// Usage in component
function ProductList() {
  const { data: products, loading, add, remove } = useIndexedDB<Product>('products');

  if (loading) return <div>Loading...</div>;

  return (
    <div>
      <h2>Products ({products.length})</h2>
      <ul>
        {products.map(product => (
          <li key={product.id}>
            {product.name} - ${product.price}
            <button onClick={() => remove(product.id)}>Delete</button>
          </li>
        ))}
      </ul>
      <button onClick={() => add({ id: Date.now().toString(), name: 'New Product', price: 99 })}>
        Add Product
      </button>
    </div>
  );
}

Example: Dexie.js for advanced queries

// Installation
npm install dexie

// db.ts
import Dexie, { Table } from 'dexie';

interface Product {
  id?: number;
  name: string;
  price: number;
  category: string;
  tags: string[];
  inStock: boolean;
}

class MyDatabase extends Dexie {
  products!: Table<Product>;

  constructor() {
    super('MyAppDB');
    this.version(1).stores({
      products: '++id, name, price, category, *tags, inStock'
      // ++ = auto-increment
      // * = multi-entry index (for arrays)
    });
  }
}

export const db = new MyDatabase();

// Complex queries
export async function searchProducts(query: string) {
  return db.products
    .where('name')
    .startsWithIgnoreCase(query)
    .or('category')
    .equalsIgnoreCase(query)
    .toArray();
}

export async function getInStockProducts() {
  return db.products.where('inStock').equals(1).toArray();
}

export async function getProductsByTags(tags: string[]) {
  return db.products.where('tags').anyOf(tags).toArray();
}

export async function getAffordableProducts(maxPrice: number) {
  return db.products.where('price').below(maxPrice).toArray();
}

// Bulk operations
export async function bulkUpdate() {
  await db.products.where('category').equals('Electronics').modify({
    inStock: false
  });
}

// Transactions
export async function transferStock(fromProductId: number, toProductId: number) {
  await db.transaction('rw', db.products, async () => {
    const fromProduct = await db.products.get(fromProductId);
    const toProduct = await db.products.get(toProductId);

    if (fromProduct && toProduct) {
      await db.products.update(fromProductId, { inStock: false });
      await db.products.update(toProductId, { inStock: true });
    }
  });
}

// Hooks integration
import { useLiveQuery } from 'dexie-react-hooks';

function ProductList() {
  const products = useLiveQuery(() => db.products.toArray());

  if (!products) return <div>Loading...</div>;

  return (
    <ul>
      {products.map(p => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}
Storage Limits: IndexedDB typically allows 50% of available disk space. Chrome/Edge: ~60% of available storage. Firefox: ~50%. Monitor quota with navigator.storage.estimate(). Implement cleanup strategies for old data.

18.6 App Shell Architecture Performance

Implement App Shell pattern for instant loading with cached static shell and dynamic content for optimal perceived performance.

Component Caching Strategy Priority Update Frequency
App Shell (HTML/CSS/JS) Cache First, pre-cache Critical On deployment
Static Assets Cache First High On version change
Dynamic Content Network First Medium Real-time
API Responses Stale While Revalidate Medium Background
User Data IndexedDB High On change

Example: App Shell structure with React

// App shell components (always cached)
// AppShell.tsx
import { Suspense, lazy } from 'react';
import { Header } from './Header'; // Pre-cached
import { Sidebar } from './Sidebar'; // Pre-cached
import { Footer } from './Footer'; // Pre-cached

// Dynamic content (loaded separately)
const DynamicContent = lazy(() => import('./DynamicContent'));

export function AppShell() {
  return (
    <div className="app-shell">
      <Header />
      
      <div className="main-content">
        <Sidebar />
        
        <main>
          <Suspense fallback={<SkeletonLoader />}>
            <DynamicContent />
          </Suspense>
        </main>
      </div>
      
      <Footer />
    </div>
  );
}

// index.html - Minimal critical HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My PWA</title>
  <link rel="manifest" href="/manifest.json">
  
  <!-- Inline critical CSS for instant render -->
  <style>
    .app-shell { display: flex; flex-direction: column; min-height: 100vh; }
    .skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); }
    /* ... minimal styles ... */
  </style>
</head>
<body>
  <div id="root">
    <!-- Initial shell renders immediately from cache -->
    <div class="app-shell">
      <header class="skeleton"></header>
      <main class="skeleton"></main>
      <footer class="skeleton"></footer>
    </div>
  </div>
  <script type="module" src="/src/main.tsx"></script>
</body>
</html>

Example: Service worker for App Shell caching

// service-worker.js with Workbox
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute, NavigationRoute } from 'workbox-routing';
import { NetworkFirst, CacheFirst, StaleWhileRevalidate } from 'workbox-strategies';

// Pre-cache app shell
const APP_SHELL_CACHE = 'app-shell-v1';
const appShellFiles = [
  '/',
  '/index.html',
  '/styles/main.css',
  '/scripts/app.js',
  '/offline.html'
];

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(APP_SHELL_CACHE).then((cache) => {
      return cache.addAll(appShellFiles);
    })
  );
  self.skipWaiting();
});

// Serve app shell for all navigation requests
registerRoute(
  new NavigationRoute(
    new NetworkFirst({
      cacheName: APP_SHELL_CACHE,
      plugins: [
        {
          cacheWillUpdate: async ({ response }) => {
            // Only cache successful responses
            return response.status === 200 ? response : null;
          }
        }
      ]
    })
  )
);

// Cache static assets
registerRoute(
  ({ request }) => request.destination === 'style' || 
                    request.destination === 'script' ||
                    request.destination === 'font',
  new CacheFirst({
    cacheName: 'static-assets-v1',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 60,
        maxAgeSeconds: 30 * 24 * 60 * 60 // 30 days
      })
    ]
  })
);

// Dynamic content with network priority
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst({
    cacheName: 'api-cache-v1',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 50,
        maxAgeSeconds: 5 * 60 // 5 minutes
      })
    ]
  })
);

// Images with cache priority
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'image-cache-v1',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 100,
        maxAgeSeconds: 7 * 24 * 60 * 60 // 7 days
      })
    ]
  })
);

Example: Performance optimization with App Shell

// SkeletonLoader.tsx - Immediate UI feedback
export function SkeletonLoader() {
  return (
    <div className="skeleton-container">
      <div className="skeleton skeleton-text" style={{ width: '60%' }} />
      <div className="skeleton skeleton-text" style={{ width: '80%' }} />
      <div className="skeleton skeleton-text" style={{ width: '70%' }} />
      <div className="skeleton skeleton-card" />
    </div>
  );
}

// Performance monitoring
export function measureAppShellPerformance() {
  if ('performance' in window) {
    window.addEventListener('load', () => {
      const perfData = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
      
      const metrics = {
        // Time to First Byte
        ttfb: perfData.responseStart - perfData.requestStart,
        
        // DOM Content Loaded
        domContentLoaded: perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart,
        
        // Full page load
        loadComplete: perfData.loadEventEnd - perfData.loadEventStart,
        
        // App shell render (custom)
        appShellRender: performance.now()
      };
      
      // Send to analytics
      console.log('App Shell Performance:', metrics);
      
      // Report to backend
      navigator.sendBeacon('/api/analytics/performance', JSON.stringify(metrics));
    });
  }
}

// useAppShellCache.ts - React hook
import { useEffect, useState } from 'react';

export function useAppShellCache() {
  const [cacheStatus, setCacheStatus] = useState<'checking' | 'cached' | 'error'>('checking');

  useEffect(() => {
    checkAppShellCache();
  }, []);

  const checkAppShellCache = async () => {
    try {
      const cache = await caches.open('app-shell-v1');
      const cachedRequests = await cache.keys();
      
      if (cachedRequests.length > 0) {
        setCacheStatus('cached');
      } else {
        setCacheStatus('error');
      }
    } catch (error) {
      setCacheStatus('error');
    }
  };

  const clearCache = async () => {
    await caches.delete('app-shell-v1');
    window.location.reload();
  };

  return { cacheStatus, clearCache };
}

PWA Implementation Checklist

Component Implementation Testing Impact
Service Worker Workbox with caching strategies DevTools offline mode Offline functionality
Web Manifest Complete manifest.json, install prompt Lighthouse PWA audit Installability
Push Notifications VAPID keys, push subscription, handlers Push notification test Re-engagement
Background Sync SyncManager, offline queue Offline form submit test Reliability
IndexedDB idb/Dexie for structured storage Storage quota checks Data persistence
App Shell Pre-cached shell, dynamic content Performance metrics Fast loading

19. Modern Development Best Practices

19.1 Clean Code Principles SOLID

Apply SOLID principles and clean code practices to write maintainable, testable, and scalable frontend applications.

Principle Definition Frontend Application Violation Example
Single Responsibility One reason to change Component does one thing well Component handles UI + API + state
Open/Closed Open for extension, closed for modification Use composition, HOCs, hooks Modify existing code for new features
Liskov Substitution Subtypes must be substitutable Consistent component interfaces Child breaks parent's contract
Interface Segregation Small, specific interfaces Focused props interfaces Large props with many optionals
Dependency Inversion Depend on abstractions Inject dependencies via props/context Hard-coded dependencies

Example: Single Responsibility Principle

// ❌ Bad: Component doing too much
function UserDashboard() {
  const [user, setUser] = useState(null);
  const [posts, setPosts] = useState([]);
  const [comments, setComments] = useState([]);
  
  useEffect(() => {
    // Fetching data
    fetch('/api/user').then(r => r.json()).then(setUser);
    fetch('/api/posts').then(r => r.json()).then(setPosts);
    fetch('/api/comments').then(r => r.json()).then(setComments);
  }, []);
  
  // Data transformation logic
  const processedPosts = posts.map(post => ({
    ...post,
    commentCount: comments.filter(c => c.postId === post.id).length
  }));
  
  // Rendering complex UI
  return (
    <div>
      <header>{user?.name}</header>
      <div>{/* Complex post list */}</div>
      <div>{/* Complex comment section */}</div>
    </div>
  );
}

// ✅ Good: Separated responsibilities
// Custom hook for data fetching
function useUserData() {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetch('/api/user').then(r => r.json()).then(setUser);
  }, []);
  
  return user;
}

function usePosts() {
  const [posts, setPosts] = useState([]);
  
  useEffect(() => {
    fetch('/api/posts').then(r => r.json()).then(setPosts);
  }, []);
  
  return posts;
}

// Separate component for user header
function UserHeader({ user }) {
  return <header>{user?.name}</header>;
}

// Separate component for posts
function PostList({ posts }) {
  return <div>{posts.map(post => <PostCard key={post.id} post={post} />)}</div>;
}

// Main component just orchestrates
function UserDashboard() {
  const user = useUserData();
  const posts = usePosts();
  
  return (
    <div>
      <UserHeader user={user} />
      <PostList posts={posts} />
    </div>
  );
}

Example: Open/Closed Principle with composition

// ❌ Bad: Modifying component for each variation
function Button({ type, icon, loading, disabled, onClick }) {
  if (loading) {
    return <button disabled><Spinner /> Loading...</button>;
  }
  
  if (type === 'primary') {
    return <button className="btn-primary" onClick={onClick}>{icon} Click</button>;
  }
  
  if (type === 'secondary') {
    return <button className="btn-secondary" onClick={onClick}>Click</button>;
  }
  
  // Adding new types requires modifying this component
}

// ✅ Good: Open for extension via composition
function Button({ children, disabled, onClick, className }) {
  return (
    <button 
      className={`btn ${className}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

// Extend via composition without modifying Button
function PrimaryButton({ children, ...props }) {
  return <Button className="btn-primary" {...props}>{children}</Button>;
}

function SecondaryButton({ children, ...props }) {
  return <Button className="btn-secondary" {...props}>{children}</Button>;
}

function LoadingButton({ loading, children, ...props }) {
  return (
    <Button disabled={loading} {...props}>
      {loading ? <><Spinner /> Loading...</> : children}
    </Button>
  );
}

function IconButton({ icon, children, ...props }) {
  return (
    <Button {...props}>
      {icon} {children}
    </Button>
  );
}

// Easy to add new variants without touching base Button
function DangerButton({ children, ...props }) {
  return <Button className="btn-danger" {...props}>{children}</Button>;
}

Example: Dependency Inversion with dependency injection

// ❌ Bad: Hard-coded dependencies
function UserList() {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    // Directly coupled to specific API implementation
    axios.get('https://api.example.com/users')
      .then(response => setUsers(response.data));
  }, []);
  
  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}

// ✅ Good: Depend on abstraction (interface/contract)
// Abstract API service
interface UserService {
  getUsers(): Promise<User[]>;
}

// Concrete implementation
class ApiUserService implements UserService {
  async getUsers() {
    const response = await axios.get('https://api.example.com/users');
    return response.data;
  }
}

// Mock implementation for testing
class MockUserService implements UserService {
  async getUsers() {
    return [
      { id: 1, name: 'John' },
      { id: 2, name: 'Jane' }
    ];
  }
}

// Component depends on abstraction, not concrete implementation
function UserList({ userService }: { userService: UserService }) {
  const [users, setUsers] = useState<User[]>([]);
  
  useEffect(() => {
    userService.getUsers().then(setUsers);
  }, [userService]);
  
  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}

// Usage with dependency injection
function App() {
  const userService = new ApiUserService(); // or MockUserService for testing
  return <UserList userService={userService} />;
}

// Even better: Use context for DI
const ServiceContext = createContext<UserService>(new ApiUserService());

function UserList() {
  const userService = useContext(ServiceContext);
  const [users, setUsers] = useState<User[]>([]);
  
  useEffect(() => {
    userService.getUsers().then(setUsers);
  }, [userService]);
  
  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
Clean Code Guidelines: Functions should be small (5-20 lines), do one thing. Names should be descriptive. Avoid magic numbers. Comments explain why, not what. DRY (Don't Repeat Yourself). KISS (Keep It Simple, Stupid).

19.2 Design Patterns Factory Observer

Apply proven design patterns to solve common frontend architecture challenges with reusable, maintainable solutions.

Pattern Purpose Frontend Use Case Implementation
Factory Pattern Object creation abstraction Component factories, form builders Function returns components
Observer Pattern Event-driven updates State management, pub/sub Subscribe/notify pattern
Singleton Pattern Single instance API client, config, store Module exports instance
Strategy Pattern Interchangeable algorithms Payment methods, validators Interface + implementations
Decorator Pattern Add behavior dynamically HOCs, wrapper components Enhance component/function
Facade Pattern Simplified interface API wrappers, complex libs Wrapper with simple API

Example: Factory Pattern for dynamic component creation

// Factory function to create form fields based on config
type FieldType = 'text' | 'email' | 'number' | 'select' | 'checkbox';

interface FieldConfig {
  type: FieldType;
  name: string;
  label: string;
  options?: { value: string; label: string }[];
  validation?: any;
}

function createFormField(config: FieldConfig) {
  switch (config.type) {
    case 'text':
    case 'email':
    case 'number':
      return <TextInput key={config.name} {...config} />;
    
    case 'select':
      return <SelectInput key={config.name} {...config} />;
    
    case 'checkbox':
      return <CheckboxInput key={config.name} {...config} />;
    
    default:
      throw new Error(`Unknown field type: ${config.type}`);
  }
}

// Form builder using factory
function DynamicForm({ fields }: { fields: FieldConfig[] }) {
  return (
    <form>
      {fields.map(field => createFormField(field))}
      <button type="submit">Submit</button>
    </form>
  );
}

// Usage
const formConfig: FieldConfig[] = [
  { type: 'text', name: 'name', label: 'Name' },
  { type: 'email', name: 'email', label: 'Email' },
  { type: 'select', name: 'country', label: 'Country', options: [
    { value: 'us', label: 'United States' },
    { value: 'uk', label: 'United Kingdom' }
  ]}
];

<DynamicForm fields={formConfig} />

// Chart factory example
function createChart(type: 'line' | 'bar' | 'pie', data: any) {
  const chartComponents = {
    line: LineChart,
    bar: BarChart,
    pie: PieChart
  };
  
  const ChartComponent = chartComponents[type];
  return <ChartComponent data={data} />;
}

Example: Observer Pattern for event system

// Event bus implementation (Observer pattern)
class EventBus {
  private events: Map<string, Set<Function>> = new Map();
  
  subscribe(event: string, callback: Function): () => void {
    if (!this.events.has(event)) {
      this.events.set(event, new Set());
    }
    
    this.events.get(event)!.add(callback);
    
    // Return unsubscribe function
    return () => {
      this.events.get(event)?.delete(callback);
    };
  }
  
  publish(event: string, data?: any): void {
    const callbacks = this.events.get(event);
    if (callbacks) {
      callbacks.forEach(callback => callback(data));
    }
  }
  
  clear(event?: string): void {
    if (event) {
      this.events.delete(event);
    } else {
      this.events.clear();
    }
  }
}

export const eventBus = new EventBus();

// Usage: Shopping cart example
// CartButton.tsx
function CartButton() {
  const addToCart = (product: Product) => {
    eventBus.publish('cart:add', product);
  };
  
  return <button onClick={() => addToCart(product)}>Add to Cart</button>;
}

// CartBadge.tsx - Observes cart events
function CartBadge() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    const unsubscribe = eventBus.subscribe('cart:add', () => {
      setCount(prev => prev + 1);
    });
    
    return unsubscribe; // Cleanup on unmount
  }, []);
  
  return <div className="badge">{count}</div>;
}

// State management with observer pattern
class Store {
  private state: any;
  private listeners = new Set<Function>();
  
  getState() {
    return this.state;
  }
  
  setState(newState: any) {
    this.state = { ...this.state, ...newState };
    this.notify();
  }
  
  subscribe(listener: Function) {
    this.listeners.add(listener);
    return () => this.listeners.delete(listener);
  }
  
  private notify() {
    this.listeners.forEach(listener => listener(this.state));
  }
}

const store = new Store();

// React hook for store
function useStore() {
  const [state, setState] = useState(store.getState());
  
  useEffect(() => {
    return store.subscribe((newState: any) => {
      setState(newState);
    });
  }, []);
  
  return [state, (newState: any) => store.setState(newState)];
}

Example: Strategy Pattern for payment processing

// Strategy interface
interface PaymentStrategy {
  processPayment(amount: number): Promise<PaymentResult>;
  validate(): boolean;
}

// Concrete strategies
class CreditCardPayment implements PaymentStrategy {
  constructor(private cardNumber: string, private cvv: string) {}
  
  validate(): boolean {
    return this.cardNumber.length === 16 && this.cvv.length === 3;
  }
  
  async processPayment(amount: number): Promise<PaymentResult> {
    // Credit card processing logic
    const response = await fetch('/api/payment/credit-card', {
      method: 'POST',
      body: JSON.stringify({ cardNumber: this.cardNumber, amount })
    });
    return response.json();
  }
}

class PayPalPayment implements PaymentStrategy {
  constructor(private email: string) {}
  
  validate(): boolean {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email);
  }
  
  async processPayment(amount: number): Promise<PaymentResult> {
    // PayPal processing logic
    const response = await fetch('/api/payment/paypal', {
      method: 'POST',
      body: JSON.stringify({ email: this.email, amount })
    });
    return response.json();
  }
}

class CryptoPayment implements PaymentStrategy {
  constructor(private walletAddress: string) {}
  
  validate(): boolean {
    return this.walletAddress.length === 42;
  }
  
  async processPayment(amount: number): Promise<PaymentResult> {
    // Cryptocurrency processing logic
    const response = await fetch('/api/payment/crypto', {
      method: 'POST',
      body: JSON.stringify({ wallet: this.walletAddress, amount })
    });
    return response.json();
  }
}

// Context that uses strategy
class PaymentProcessor {
  constructor(private strategy: PaymentStrategy) {}
  
  setStrategy(strategy: PaymentStrategy) {
    this.strategy = strategy;
  }
  
  async execute(amount: number): Promise<PaymentResult> {
    if (!this.strategy.validate()) {
      throw new Error('Invalid payment details');
    }
    
    return this.strategy.processPayment(amount);
  }
}

// React component using strategies
function CheckoutForm() {
  const [paymentMethod, setPaymentMethod] = useState<'card' | 'paypal' | 'crypto'>('card');
  const [processor] = useState(new PaymentProcessor(new CreditCardPayment('', '')));
  
  const handlePayment = async (amount: number) => {
    // Select strategy based on user choice
    let strategy: PaymentStrategy;
    
    switch (paymentMethod) {
      case 'card':
        strategy = new CreditCardPayment(cardNumber, cvv);
        break;
      case 'paypal':
        strategy = new PayPalPayment(email);
        break;
      case 'crypto':
        strategy = new CryptoPayment(walletAddress);
        break;
    }
    
    processor.setStrategy(strategy);
    const result = await processor.execute(amount);
    
    if (result.success) {
      alert('Payment successful!');
    }
  };
  
  return (
    <form>
      <select onChange={(e) => setPaymentMethod(e.target.value as any)}>
        <option value="card">Credit Card</option>
        <option value="paypal">PayPal</option>
        <option value="crypto">Cryptocurrency</option>
      </select>
      
      {/* Render appropriate form fields based on strategy */}
      
      <button onClick={() => handlePayment(99.99)}>Pay Now</button>
    </form>
  );
}
Pattern Overuse: Don't force patterns where they're not needed. Patterns add complexity and abstraction. Use them to solve specific problems, not to demonstrate knowledge. Start simple, refactor to patterns when needed.

19.3 Performance Budgets Lighthouse CI

Define and enforce performance budgets with automated testing to prevent performance regressions in production.

Metric Good Target Tool Impact
First Contentful Paint (FCP) <1.8s Lighthouse, WebPageTest Perceived speed
Largest Contentful Paint (LCP) <2.5s Core Web Vitals Loading performance
Time to Interactive (TTI) <3.8s Lighthouse Interactivity
Total Blocking Time (TBT) <200ms Lighthouse Responsiveness
Cumulative Layout Shift (CLS) <0.1 Core Web Vitals Visual stability
JavaScript Bundle Size <200KB (gzipped) Webpack Bundle Analyzer Load time

Example: Lighthouse CI setup for automated performance testing

// Installation
npm install -D @lhci/cli

// lighthouserc.json
{
  "ci": {
    "collect": {
      "startServerCommand": "npm run build && npm run preview",
      "url": [
        "http://localhost:4173/",
        "http://localhost:4173/products",
        "http://localhost:4173/checkout"
      ],
      "numberOfRuns": 3
    },
    "assert": {
      "preset": "lighthouse:recommended",
      "assertions": {
        // Performance budgets
        "first-contentful-paint": ["error", { "maxNumericValue": 1800 }],
        "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
        "interactive": ["error", { "maxNumericValue": 3800 }],
        "speed-index": ["error", { "maxNumericValue": 3400 }],
        "total-blocking-time": ["error", { "maxNumericValue": 200 }],
        "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
        
        // Resource budgets
        "resource-summary:script:size": ["error", { "maxNumericValue": 204800 }], // 200KB
        "resource-summary:stylesheet:size": ["error", { "maxNumericValue": 51200 }], // 50KB
        "resource-summary:image:size": ["error", { "maxNumericValue": 512000 }], // 500KB
        "resource-summary:total:size": ["error", { "maxNumericValue": 1048576 }], // 1MB
        
        // Best practices
        "uses-responsive-images": "error",
        "offscreen-images": "error",
        "modern-image-formats": "warn",
        "uses-text-compression": "error",
        "unused-javascript": "warn",
        
        // Accessibility
        "color-contrast": "error",
        "heading-order": "error",
        "label": "error"
      }
    },
    "upload": {
      "target": "temporary-public-storage"
    }
  }
}

// package.json scripts
{
  "scripts": {
    "lhci:collect": "lhci collect",
    "lhci:assert": "lhci assert",
    "lhci:upload": "lhci upload",
    "lhci:full": "lhci collect && lhci assert && lhci upload"
  }
}

Example: GitHub Actions CI workflow with Lighthouse

// .github/workflows/lighthouse-ci.yml
name: Lighthouse CI

on:
  pull_request:
    branches: [main]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
      
      - name: Run Lighthouse CI
        run: |
          npm install -g @lhci/cli
          lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
      
      - name: Upload results
        uses: actions/upload-artifact@v3
        with:
          name: lighthouse-results
          path: .lighthouseci/
      
      - name: Comment PR
        uses: treosh/lighthouse-ci-action@v9
        with:
          urls: |
            http://localhost:4173
            http://localhost:4173/products
          uploadArtifacts: true
          temporaryPublicStorage: true

Example: Webpack performance budgets

// webpack.config.js
module.exports = {
  performance: {
    maxAssetSize: 244000, // 244KB
    maxEntrypointSize: 244000,
    hints: 'error', // or 'warning'
    assetFilter: function(assetFilename) {
      // Only check JS and CSS files
      return /\.(js|css)$/.test(assetFilename);
    }
  },
  
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10
        },
        common: {
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true
        }
      }
    }
  }
};

// Next.js config with budgets
// next.config.js
module.exports = {
  experimental: {
    performanceBudgets: [
      {
        path: '/',
        maxSize: {
          total: 200 * 1024, // 200KB
          javascript: 150 * 1024,
          css: 50 * 1024
        }
      },
      {
        path: '/products',
        maxSize: {
          total: 250 * 1024
        }
      }
    ]
  }
};

// bundlesize package
// package.json
{
  "bundlesize": [
    {
      "path": "./dist/main.*.js",
      "maxSize": "150 kB"
    },
    {
      "path": "./dist/vendor.*.js",
      "maxSize": "50 kB"
    },
    {
      "path": "./dist/styles.*.css",
      "maxSize": "30 kB"
    }
  ]
}

// CI script
npm install -g bundlesize
bundlesize
Monitoring Strategy: Set budgets 20% lower than current performance to encourage optimization. Run Lighthouse on every PR. Track Core Web Vitals in production with Real User Monitoring (RUM). Review budgets quarterly.

19.4 Code Splitting Route Based

Implement route-based and component-based code splitting to reduce initial bundle size and improve load performance.

Technique When to Use Benefits Trade-offs
Route-based Splitting Different pages/routes Smaller initial bundle, faster FCP Navigation delay
Component-based Splitting Large components (modals, charts) Load on demand Complexity
Vendor Splitting Third-party libraries Better caching More requests
Dynamic Import Conditional features Load only when needed Network waterfall

Example: React route-based code splitting

// App.tsx - Route-based lazy loading
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

// Eagerly loaded (always needed)
import { Header } from './components/Header';
import { Footer } from './components/Footer';

// Lazy loaded routes (loaded on demand)
const Home = lazy(() => import('./pages/Home'));
const Products = lazy(() => import('./pages/Products'));
const ProductDetail = lazy(() => import('./pages/ProductDetail'));
const Checkout = lazy(() => import('./pages/Checkout'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Admin = lazy(() => import('./pages/Admin'));

// Loading fallback
function PageLoader() {
  return (
    <div className="page-loader">
      <div className="spinner" />
      <p>Loading...</p>
    </div>
  );
}

export function App() {
  return (
    <BrowserRouter>
      <Header />
      
      <main>
        <Suspense fallback={<PageLoader />}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/products" element={<Products />} />
            <Route path="/products/:id" element={<ProductDetail />} />
            <Route path="/checkout" element={<Checkout />} />
            <Route path="/dashboard" element={<Dashboard />} />
            <Route path="/admin/*" element={<Admin />} />
          </Routes>
        </Suspense>
      </main>
      
      <Footer />
    </BrowserRouter>
  );
}

// Result: Each route is a separate chunk
// home.chunk.js, products.chunk.js, checkout.chunk.js, etc.

Example: Component-based code splitting with prefetching

// Lazy load heavy components
import { lazy, Suspense } from 'react';

const HeavyChart = lazy(() => import('./components/HeavyChart'));
const VideoPlayer = lazy(() => import('./components/VideoPlayer'));
const RichTextEditor = lazy(() => import('./components/RichTextEditor'));

// Prefetch on hover for better UX
function ProductPage() {
  const [showChart, setShowChart] = useState(false);
  
  const handleMouseEnter = () => {
    // Prefetch chart component
    import('./components/HeavyChart');
  };
  
  return (
    <div>
      <h1>Product Analytics</h1>
      
      <button 
        onClick={() => setShowChart(true)}
        onMouseEnter={handleMouseEnter}
      >
        Show Chart
      </button>
      
      {showChart && (
        <Suspense fallback={<div>Loading chart...</div>}>
          <HeavyChart data={data} />
        </Suspense>
      )}
    </div>
  );
}

// Conditional feature loading
function AdminPanel() {
  const { user } = useAuth();
  const [AdminTools, setAdminTools] = useState(null);
  
  useEffect(() => {
    if (user?.role === 'admin') {
      // Only load admin tools for admin users
      import('./components/AdminTools').then(module => {
        setAdminTools(() => module.default);
      });
    }
  }, [user]);
  
  if (!user) return <Login />;
  if (user.role !== 'admin') return <Forbidden />;
  if (!AdminTools) return <Loading />;
  
  return <AdminTools />;
}

// Named exports lazy loading
const { AdvancedFilters, DataExport } = lazy(() => 
  import('./components/AdvancedFeatures').then(module => ({
    default: {
      AdvancedFilters: module.AdvancedFilters,
      DataExport: module.DataExport
    }
  }))
);

Example: Next.js dynamic imports with loading states

// Next.js dynamic import
import dynamic from 'next/dynamic';

// Basic dynamic import
const DynamicComponent = dynamic(() => import('./components/Heavy'), {
  loading: () => <p>Loading...</p>,
  ssr: false // Disable SSR for this component
});

// Import with named export
const DynamicChart = dynamic(
  () => import('./components/Charts').then(mod => mod.LineChart),
  { loading: () => <ChartSkeleton /> }
);

// Conditional dynamic import
function Dashboard() {
  const DynamicMap = dynamic(() => import('./components/Map'), {
    loading: () => <MapSkeleton />,
    ssr: false
  });
  
  return (
    <div>
      <h1>Dashboard</h1>
      <DynamicMap center={[51.505, -0.09]} zoom={13} />
    </div>
  );
}

// Multiple dynamic components
const [Modal, Tooltip, Drawer] = [
  dynamic(() => import('./components/Modal')),
  dynamic(() => import('./components/Tooltip')),
  dynamic(() => import('./components/Drawer'))
];

// Webpack magic comments for better chunk names
const ProductDetail = lazy(() => 
  import(
    /* webpackChunkName: "product-detail" */
    /* webpackPrefetch: true */
    './pages/ProductDetail'
  )
);
Best Practices: Split routes by default. Lazy load modals, charts, rich editors. Prefetch on hover/focus. Use loading skeletons. Keep critical path synchronous. Monitor chunk sizes with bundle analyzer.

19.5 Tree Shaking Dead Code Elimination

Eliminate unused code from bundles through proper module exports, sideEffects configuration, and build optimizations.

Technique Purpose Configuration Impact
ES6 Modules Enable static analysis Use import/export Required for tree shaking
sideEffects Flag Mark pure modules package.json Aggressive elimination
Named Exports Import only what's needed Avoid default exports Better tree shaking
Production Mode Enable optimizations NODE_ENV=production Removes dev code

Example: Proper module structure for tree shaking

// ❌ Bad: Default export with everything
// utils.js
export default {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b,
  multiply: (a, b) => a * b,
  divide: (a, b) => a / b,
  // ... 50 more functions
};

// Usage imports everything (no tree shaking)
import utils from './utils';
utils.add(1, 2);

// ✅ Good: Named exports
// utils.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;
export const divide = (a, b) => a / b;

// Usage imports only what's needed (tree shaken)
import { add } from './utils';
add(1, 2);

// ❌ Bad: Barrel exports without re-exports
// index.js
export * from './utils';
export * from './validators';
export * from './formatters';

// ✅ Good: Direct imports or selective barrel exports
// Import directly from source
import { add } from './utils/math';
import { validateEmail } from './validators/email';

// Or use selective barrel exports
// index.js
export { add, subtract } from './utils/math';
export { validateEmail, validatePhone } from './validators';

// ❌ Bad: Side effects prevent tree shaking
// module.js
import './styles.css'; // Side effect
console.log('Module loaded'); // Side effect

export const doSomething = () => {};

// ✅ Good: Pure module
// module.js
export const doSomething = () => {};

// Import styles separately when needed
// App.tsx
import './styles.css';
import { doSomething } from './module';

Example: package.json sideEffects configuration

// package.json - Library configuration
{
  "name": "my-library",
  "version": "1.0.0",
  "main": "dist/index.js",
  "module": "dist/index.esm.js",
  "sideEffects": false, // No side effects, safe to tree shake
  "exports": {
    ".": {
      "import": "./dist/index.esm.js",
      "require": "./dist/index.js"
    },
    "./utils": {
      "import": "./dist/utils.esm.js",
      "require": "./dist/utils.js"
    }
  }
}

// Or specify files with side effects
{
  "sideEffects": [
    "*.css",
    "*.scss",
    "./src/polyfills.js"
  ]
}

// Webpack configuration
module.exports = {
  mode: 'production', // Enables tree shaking
  optimization: {
    usedExports: true, // Mark unused exports
    minimize: true, // Remove unused code
    sideEffects: true // Respect package.json sideEffects
  }
};

// Rollup configuration
export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'
  },
  plugins: [
    resolve(),
    commonjs(),
    terser() // Minify and tree shake
  ],
  treeshake: {
    moduleSideEffects: false,
    propertyReadSideEffects: false
  }
};

Example: Optimizing library imports for tree shaking

// ❌ Bad: Import entire library
import _ from 'lodash'; // Imports all of lodash (~70KB)
import * as MUI from '@mui/material'; // Imports everything
import moment from 'moment'; // Imports full moment.js

_.debounce(fn, 300);
const button = <MUI.Button />;
moment().format();

// ✅ Good: Import specific functions/components
import debounce from 'lodash/debounce'; // Only debounce (~2KB)
import Button from '@mui/material/Button'; // Only Button component
import dayjs from 'dayjs'; // Smaller alternative to moment

debounce(fn, 300);
const button = <Button />;
dayjs().format();

// ✅ Better: Use libraries with good tree shaking
// lodash-es (ES modules version)
import { debounce, throttle } from 'lodash-es';

// date-fns (tree-shakeable by default)
import { format, addDays } from 'date-fns';

// ✅ Best: Babel plugin for automatic optimization
// .babelrc
{
  "plugins": [
    ["import", {
      "libraryName": "lodash",
      "libraryDirectory": "",
      "camel2DashComponentName": false
    }],
    ["import", {
      "libraryName": "@mui/material",
      "libraryDirectory": "",
      "camel2DashComponentName": false
    }]
  ]
}

// Write normal imports, babel transforms them
import { Button, TextField } from '@mui/material';
// Becomes: 
// import Button from '@mui/material/Button';
// import TextField from '@mui/material/TextField';

// Next.js optimized imports (built-in)
// next.config.js
module.exports = {
  modularizeImports: {
    '@mui/material': {
      transform: '@mui/material/{{member}}'
    },
    'lodash': {
      transform: 'lodash/{{member}}'
    }
  }
};
Common Pitfalls: CommonJS modules can't be tree shaken. Barrel exports (index.js) can prevent tree shaking. Side effects (CSS imports, global mutations) prevent elimination. Always analyze bundle with webpack-bundle-analyzer.

19.6 Bundle Optimization Webpack Rollup

Optimize production bundles with advanced techniques: minification, compression, code splitting, and caching strategies.

Optimization Technique Tool Benefit
Minification Remove whitespace, shorten names Terser, esbuild 30-50% size reduction
Compression Gzip, Brotli compression-webpack-plugin 60-80% size reduction
Code Splitting Separate vendor/async chunks SplitChunksPlugin Better caching
Content Hashing Filename based on content [contenthash] Long-term caching
Scope Hoisting Flatten module scope ModuleConcatenationPlugin Smaller bundle, faster

Example: Advanced Webpack optimization configuration

// webpack.config.js - Production optimization
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  mode: 'production',
  
  output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js',
    path: path.resolve(__dirname, 'dist'),
    clean: true,
    publicPath: '/assets/'
  },
  
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true, // Remove console.log
            drop_debugger: true,
            pure_funcs: ['console.info', 'console.debug']
          },
          mangle: {
            safari10: true
          },
          format: {
            comments: false
          }
        },
        extractComments: false,
        parallel: true
      })
    ],
    
    // Split chunks for better caching
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        // Vendor libraries
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10,
          reuseExistingChunk: true
        },
        // Common code shared across chunks
        common: {
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true,
          enforce: true
        },
        // Large libraries in separate chunks
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'react',
          priority: 20
        },
        // UI libraries
        mui: {
          test: /[\\/]node_modules[\\/]@mui[\\/]/,
          name: 'mui',
          priority: 15
        }
      },
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      minSize: 20000,
      maxSize: 244000
    },
    
    // Runtime chunk for better caching
    runtimeChunk: {
      name: 'runtime'
    },
    
    // Module IDs based on path
    moduleIds: 'deterministic'
  },
  
  plugins: [
    // Gzip compression
    new CompressionPlugin({
      filename: '[path][base].gz',
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 10240,
      minRatio: 0.8
    }),
    
    // Brotli compression (better than gzip)
    new CompressionPlugin({
      filename: '[path][base].br',
      algorithm: 'brotliCompress',
      test: /\.(js|css|html|svg)$/,
      compressionOptions: {
        level: 11
      },
      threshold: 10240,
      minRatio: 0.8
    }),
    
    // Bundle analysis
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      openAnalyzer: false,
      reportFilename: 'bundle-report.html'
    })
  ],
  
  performance: {
    maxAssetSize: 244000,
    maxEntrypointSize: 244000,
    hints: 'warning'
  }
};

Example: Vite optimization configuration

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { visualizer } from 'rollup-plugin-visualizer';
import compression from 'vite-plugin-compression';

export default defineConfig({
  plugins: [
    react(),
    
    // Gzip compression
    compression({
      algorithm: 'gzip',
      ext: '.gz'
    }),
    
    // Brotli compression
    compression({
      algorithm: 'brotliCompress',
      ext: '.br'
    }),
    
    // Bundle analysis
    visualizer({
      open: false,
      filename: 'dist/stats.html',
      gzipSize: true,
      brotliSize: true
    })
  ],
  
  build: {
    target: 'es2020',
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    },
    
    // Chunk splitting
    rollupOptions: {
      output: {
        manualChunks: {
          // Vendor chunks
          'react-vendor': ['react', 'react-dom', 'react-router-dom'],
          'ui-vendor': ['@mui/material', '@emotion/react'],
          'utils': ['lodash-es', 'date-fns']
        },
        
        // Chunk naming with content hash
        chunkFileNames: 'assets/[name].[hash].js',
        entryFileNames: 'assets/[name].[hash].js',
        assetFileNames: 'assets/[name].[hash].[ext]'
      }
    },
    
    // Chunk size warnings
    chunkSizeWarningLimit: 500,
    
    // Source maps for production debugging
    sourcemap: 'hidden',
    
    // CSS code splitting
    cssCodeSplit: true,
    
    // Asset inlining threshold
    assetsInlineLimit: 4096
  },
  
  // Optimize dependencies
  optimizeDeps: {
    include: ['react', 'react-dom'],
    exclude: ['@vite/client', '@vite/env']
  }
});

Example: Advanced bundle analysis and optimization

// Analyze bundle with webpack-bundle-analyzer
npm install -D webpack-bundle-analyzer

// Run analysis
npx webpack --profile --json > stats.json
npx webpack-bundle-analyzer stats.json

// Identify optimization opportunities:
// 1. Large dependencies (replace with lighter alternatives)
// 2. Duplicate code (adjust splitChunks)
// 3. Unused imports (tree shaking issues)
// 4. Large assets (optimize images, fonts)

// Source map explorer (alternative)
npm install -D source-map-explorer

// package.json
{
  "scripts": {
    "analyze": "source-map-explorer 'dist/*.js'"
  }
}

// Optimization strategies based on analysis:

// 1. Replace large libraries
// moment.js (67KB) → dayjs (2KB)
// lodash (71KB) → lodash-es + tree shaking
// MaterialUI lighter alternative or selective imports

// 2. Dynamic imports for large features
const Chart = lazy(() => import(
  /* webpackChunkName: "chart" */
  /* webpackPrefetch: true */
  './components/Chart'
));

// 3. Optimize images
// Use next/image or webpack image-loader
{
  test: /\.(png|jpg|jpeg)$/,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 8 * 1024 // 8KB inline limit
    }
  },
  use: [
    {
      loader: 'image-webpack-loader',
      options: {
        mozjpeg: { progressive: true, quality: 65 },
        optipng: { enabled: true },
        pngquant: { quality: [0.65, 0.90], speed: 4 },
        gifsicle: { interlaced: false },
        webp: { quality: 75 }
      }
    }
  ]
}

// 4. Preload critical chunks
<link rel="preload" href="/assets/main.js" as="script">
<link rel="prefetch" href="/assets/chart.chunk.js" as="script">

// 5. Enable HTTP/2 server push
// In your server configuration
Link: </assets/main.js>; rel=preload; as=script
Link: </assets/styles.css>; rel=preload; as=style

Bundle Optimization Checklist

Optimization Target Tool Expected Reduction
Minification JS, CSS, HTML Terser, cssnano 30-40%
Compression All text files Gzip, Brotli 60-80%
Tree Shaking Unused code Webpack, Rollup 20-50%
Code Splitting Routes, vendors Dynamic imports Initial: 40-60%
Image Optimization PNG, JPG, SVG imagemin, WebP 50-70%
Font Subsetting Web fonts glyphhanger 70-90%

20. Deployment Production Implementation

20.1 Vercel Netlify Jamstack Hosting

Deploy frontend applications to modern hosting platforms with automatic builds, CDN distribution, and serverless functions.

Platform Best For Key Features Pricing
Vercel Next.js, React apps Edge network, ISR, serverless Free hobby, $20+/mo pro
Netlify Static sites, Jamstack Form handling, split testing Free starter, $19+/mo pro
Cloudflare Pages Static sites, workers Unlimited bandwidth, fast CDN Free unlimited
AWS Amplify Full-stack apps Backend integration, auth Pay per build
GitHub Pages Static sites, docs Free hosting, Jekyll support Free

Example: Vercel deployment configuration

// vercel.json
{
  "version": 2,
  "builds": [
    {
      "src": "package.json",
      "use": "@vercel/next"
    }
  ],
  "routes": [
    {
      "src": "/api/(.*)",
      "dest": "/api/$1"
    },
    {
      "src": "/(.*)",
      "dest": "/$1"
    }
  ],
  "env": {
    "NEXT_PUBLIC_API_URL": "https://api.example.com",
    "DATABASE_URL": "@database_url"
  },
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        },
        {
          "key": "X-Frame-Options",
          "value": "DENY"
        },
        {
          "key": "X-XSS-Protection",
          "value": "1; mode=block"
        }
      ]
    },
    {
      "source": "/static/(.*)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    }
  ],
  "redirects": [
    {
      "source": "/old-page",
      "destination": "/new-page",
      "permanent": true
    }
  ],
  "rewrites": [
    {
      "source": "/blog/:path*",
      "destination": "https://blog.example.com/:path*"
    }
  ]
}

// Deploy via CLI
npm install -g vercel
vercel login
vercel --prod

// Deploy via Git integration (automatic)
// Push to main branch → auto-deploy to production
// Push to other branches → preview deployments

// Environment variables (via Vercel Dashboard or CLI)
vercel env add NEXT_PUBLIC_API_URL production
vercel env add DATABASE_URL production

// Preview URLs for each PR
// https://my-app-git-feature-branch-username.vercel.app

Example: Netlify deployment configuration

// netlify.toml
[build]
  base = "/"
  publish = "dist"
  command = "npm run build"

[build.environment]
  NODE_VERSION = "18"
  NPM_FLAGS = "--legacy-peer-deps"

# Redirect rules
[[redirects]]
  from = "/old-url"
  to = "/new-url"
  status = 301

[[redirects]]
  from = "/api/*"
  to = "https://api.example.com/:splat"
  status = 200

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200  # SPA fallback

# Headers
[[headers]]
  for = "/*"
  [headers.values]
    X-Frame-Options = "DENY"
    X-XSS-Protection = "1; mode=block"
    X-Content-Type-Options = "nosniff"
    Referrer-Policy = "strict-origin-when-cross-origin"

[[headers]]
  for = "/static/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

# Functions (serverless)
[functions]
  directory = "netlify/functions"
  node_bundler = "esbuild"

# Dev server
[dev]
  command = "npm run dev"
  port = 3000
  publish = "dist"

# Deploy contexts
[context.production]
  environment = { NODE_ENV = "production" }

[context.deploy-preview]
  environment = { NODE_ENV = "staging" }

[context.branch-deploy]
  environment = { NODE_ENV = "development" }

# Form handling (Netlify feature)
# In HTML:
<form name="contact" method="POST" data-netlify="true">
  <input type="text" name="name" />
  <input type="email" name="email" />
  <button type="submit">Submit</button>
</form>

# Deploy via CLI
npm install -g netlify-cli
netlify login
netlify init
netlify deploy --prod

Example: Cloudflare Pages deployment

// wrangler.toml (for Pages with Functions)
name = "my-app"
compatibility_date = "2024-01-01"

[build]
  command = "npm run build"
  cwd = "."
  watch_dir = "src"

[[build.upload]]
  format = "service-worker"
  main = "./functions/[[path]].js"

[env.production]
  [env.production.vars]
    API_URL = "https://api.example.com"

# _redirects file (in public folder)
/old-url  /new-url  301
/api/*    https://api.example.com/:splat  200
/*        /index.html  200

# _headers file
/*
  X-Frame-Options: DENY
  X-XSS-Protection: 1; mode=block
  X-Content-Type-Options: nosniff

/static/*
  Cache-Control: public, max-age=31536000, immutable

# Cloudflare Pages Function
// functions/api/hello.ts
export async function onRequest(context) {
  const { request, env, params } = context;
  
  return new Response(JSON.stringify({
    message: 'Hello from Cloudflare Pages',
    time: new Date().toISOString()
  }), {
    headers: { 'Content-Type': 'application/json' }
  });
}

# Deploy via CLI
npm install -g wrangler
wrangler login
wrangler pages publish dist --project-name=my-app

# Or use Git integration (automatic)
# Connect GitHub repo → automatic deployments
Platform Selection: Vercel for Next.js apps with edge functions. Netlify for static sites with forms and split testing. Cloudflare Pages for unlimited bandwidth. All provide automatic HTTPS, CDN, preview deployments, and Git integration.

20.2 Docker Container Frontend Apps

Containerize frontend applications with Docker for consistent deployments across environments and orchestration with Kubernetes.

Strategy Use Case Benefits Complexity
Multi-stage Build Production optimization Smaller images, faster builds Medium
Nginx Serving Static files, reverse proxy Fast, efficient, caching Low
Node.js SSR Next.js, server rendering Dynamic content, APIs Medium
Docker Compose Multi-container setup Local dev, testing Medium

Example: Multi-stage Dockerfile for React app

# Dockerfile - Multi-stage build for production

# Stage 1: Build
FROM node:18-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production && \
    npm cache clean --force

# Copy source code
COPY . .

# Build app
RUN npm run build

# Stage 2: Production
FROM nginx:alpine AS production

# Copy custom nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Copy built assets from builder
COPY --from=builder /app/dist /usr/share/nginx/html

# Add healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --quiet --tries=1 --spider http://localhost:80/health || exit 1

# Expose port
EXPOSE 80

# Start nginx
CMD ["nginx", "-g", "daemon off;"]

# Build and run
# docker build -t my-app:latest .
# docker run -p 8080:80 my-app:latest

Example: Nginx configuration for SPA

# nginx.conf
server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    index index.html;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript 
               application/x-javascript application/xml+rss 
               application/javascript application/json;

    # Security headers
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Cache static assets
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # API proxy (optional)
    location /api {
        proxy_pass http://backend-service:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    # SPA fallback - serve index.html for all routes
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Health check endpoint
    location /health {
        access_log off;
        return 200 "healthy\n";
        add_header Content-Type text/plain;
    }

    # Deny access to hidden files
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }
}

Example: Next.js SSR Dockerfile

# Dockerfile for Next.js
FROM node:18-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Build Next.js app
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build

FROM node:18-alpine AS runner
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

# Create non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy necessary files
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

# Set correct permissions
USER nextjs

EXPOSE 3000

ENV PORT 3000

CMD ["node", "server.js"]

# next.config.js - Enable standalone output
module.exports = {
  output: 'standalone'
}

Example: Docker Compose for full-stack development

# docker-compose.yml
version: '3.8'

services:
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:80"
    environment:
      - REACT_APP_API_URL=http://localhost:4000
    depends_on:
      - backend
    networks:
      - app-network
    volumes:
      - ./frontend/src:/app/src  # Hot reload in dev

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "4000:4000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
      - NODE_ENV=development
    depends_on:
      - db
    networks:
      - app-network

  db:
    image: postgres:15-alpine
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=mydb
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - app-network

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - frontend
      - backend
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  postgres-data:

# Commands
# docker-compose up -d
# docker-compose down
# docker-compose logs -f frontend
# docker-compose exec frontend sh
Security Best Practices: Use multi-stage builds to reduce image size. Run as non-root user. Scan images for vulnerabilities with docker scan. Use .dockerignore to exclude unnecessary files. Pin base image versions.

20.3 CDN CloudFront Asset Optimization

Distribute static assets globally with CDN for faster load times, reduced latency, and improved user experience worldwide.

CDN Provider Key Features Best For Pricing Model
CloudFront (AWS) Global edge, Lambda@Edge AWS ecosystem integration Pay per request
Cloudflare DDoS protection, workers Security, free tier Free + paid plans
Fastly Instant purge, VCL Real-time apps Pay as you go
Akamai Largest network Enterprise scale Enterprise pricing

Example: CloudFront distribution setup with Terraform

# main.tf - CloudFront + S3 setup
resource "aws_s3_bucket" "frontend" {
  bucket = "my-app-frontend"
}

resource "aws_s3_bucket_public_access_block" "frontend" {
  bucket = aws_s3_bucket.frontend.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_cloudfront_origin_access_identity" "frontend" {
  comment = "OAI for frontend bucket"
}

resource "aws_s3_bucket_policy" "frontend" {
  bucket = aws_s3_bucket.frontend.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "AllowCloudFrontAccess"
        Effect = "Allow"
        Principal = {
          AWS = aws_cloudfront_origin_access_identity.frontend.iam_arn
        }
        Action   = "s3:GetObject"
        Resource = "${aws_s3_bucket.frontend.arn}/*"
      }
    ]
  })
}

resource "aws_cloudfront_distribution" "frontend" {
  enabled             = true
  is_ipv6_enabled     = true
  comment             = "Frontend distribution"
  default_root_object = "index.html"
  price_class         = "PriceClass_100"
  aliases             = ["www.example.com", "example.com"]

  origin {
    domain_name = aws_s3_bucket.frontend.bucket_regional_domain_name
    origin_id   = "S3-Frontend"

    s3_origin_config {
      origin_access_identity = aws_cloudfront_origin_access_identity.frontend.cloudfront_access_identity_path
    }
  }

  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "S3-Frontend"

    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
    compress               = true
  }

  # Cache behavior for static assets
  ordered_cache_behavior {
    path_pattern     = "/static/*"
    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "S3-Frontend"

    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }

    min_ttl                = 31536000
    default_ttl            = 31536000
    max_ttl                = 31536000
    compress               = true
    viewer_protocol_policy = "redirect-to-https"
  }

  # SPA fallback
  custom_error_response {
    error_code         = 404
    response_code      = 200
    response_page_path = "/index.html"
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    acm_certificate_arn      = aws_acm_certificate.cert.arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }

  tags = {
    Environment = "production"
  }
}

Example: CDN cache control headers

// Cache control strategy for different asset types

// 1. HTML files - No cache (always fresh)
Cache-Control: no-cache, no-store, must-revalidate
// Or short cache with revalidation
Cache-Control: max-age=0, must-revalidate

// 2. JavaScript/CSS with hash in filename
// main.a1b2c3d4.js, styles.e5f6g7h8.css
Cache-Control: public, max-age=31536000, immutable

// 3. Images, fonts with hash
// logo.i9j0k1l2.png
Cache-Control: public, max-age=31536000, immutable

// 4. API responses
Cache-Control: max-age=60, s-maxage=300, stale-while-revalidate=86400

// 5. Dynamic content
Cache-Control: private, max-age=0, must-revalidate

// Next.js automatic cache headers
// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=0, must-revalidate'
          }
        ]
      },
      {
        source: '/_next/static/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=31536000, immutable'
          }
        ]
      },
      {
        source: '/images/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=86400, s-maxage=604800'
          }
        ]
      }
    ];
  }
};

// Webpack output with content hash
output: {
  filename: '[name].[contenthash].js',
  chunkFilename: '[name].[contenthash].chunk.js'
}

// CloudFront cache invalidation
aws cloudfront create-invalidation \
  --distribution-id E1234ABCD5678 \
  --paths "/*"

// Selective invalidation (cheaper)
aws cloudfront create-invalidation \
  --distribution-id E1234ABCD5678 \
  --paths "/index.html" "/manifest.json"

Example: Cloudflare Workers for edge optimization

// workers/optimize-images.js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    
    // Check if WebP is supported
    const acceptHeader = request.headers.get('Accept') || '';
    const supportsWebP = acceptHeader.includes('image/webp');
    
    // Transform image format at edge
    if (url.pathname.match(/\.(jpg|png)$/)) {
      const imageRequest = new Request(request);
      
      if (supportsWebP) {
        // Serve WebP version
        const webpUrl = url.pathname.replace(/\.(jpg|png)$/, '.webp');
        return fetch(new Request(webpUrl, imageRequest));
      }
    }
    
    // Add custom headers
    const response = await fetch(request);
    const newResponse = new Response(response.body, response);
    
    // Cache static assets aggressively
    if (url.pathname.startsWith('/static/')) {
      newResponse.headers.set('Cache-Control', 'public, max-age=31536000, immutable');
    }
    
    // Security headers
    newResponse.headers.set('X-Content-Type-Options', 'nosniff');
    newResponse.headers.set('X-Frame-Options', 'DENY');
    
    return newResponse;
  }
};

// wrangler.toml
name = "image-optimizer"
main = "workers/optimize-images.js"
compatibility_date = "2024-01-01"

[env.production]
route = "example.com/*"
CDN Best Practices: Use content hashing for immutable assets. Set aggressive cache headers for static files. Invalidate cache selectively. Enable compression (Gzip/Brotli). Use HTTP/2 push for critical resources. Monitor CDN hit ratio.

20.4 Environment Variables Config

Manage environment-specific configuration securely across development, staging, and production environments.

Approach Use Case Security Complexity
.env Files Local development Low (gitignored) Low
Platform Secrets Vercel/Netlify secrets High (encrypted) Low
AWS Secrets Manager Production secrets High (encrypted, rotated) Medium
HashiCorp Vault Enterprise secrets Very High High

Example: Environment variables setup with validation

// .env.example (commit to git)
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX
DATABASE_URL=postgresql://localhost:5432/mydb
JWT_SECRET=your-secret-key-here
STRIPE_SECRET_KEY=sk_test_xxx

// .env.local (gitignored - actual values)
NEXT_PUBLIC_API_URL=http://localhost:4000
NEXT_PUBLIC_GA_ID=G-123456789
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb_dev
JWT_SECRET=super-secret-dev-key
STRIPE_SECRET_KEY=sk_test_actual_key

// .env.production (production values - use secrets manager)
NEXT_PUBLIC_API_URL=https://api.production.com
NEXT_PUBLIC_GA_ID=G-PROD123456
DATABASE_URL=@database-url-secret
JWT_SECRET=@jwt-secret
STRIPE_SECRET_KEY=@stripe-secret

// .gitignore
.env*.local
.env.production

// config/env.ts - Type-safe environment variables
import { z } from 'zod';

const envSchema = z.object({
  // Public variables (available in browser)
  NEXT_PUBLIC_API_URL: z.string().url(),
  NEXT_PUBLIC_GA_ID: z.string().optional(),
  
  // Server-only variables
  DATABASE_URL: z.string(),
  JWT_SECRET: z.string().min(32),
  STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
  
  // Environment
  NODE_ENV: z.enum(['development', 'staging', 'production']).default('development')
});

// Validate and export
const _env = envSchema.safeParse(process.env);

if (!_env.success) {
  console.error('❌ Invalid environment variables:', _env.error.format());
  throw new Error('Invalid environment variables');
}

export const env = _env.data;

// Usage
import { env } from '@/config/env';

console.log(env.NEXT_PUBLIC_API_URL); // Type-safe access
console.log(env.JWT_SECRET); // Only available server-side

Example: Multiple environment configurations

// config/environments.ts
interface AppConfig {
  apiUrl: string;
  environment: 'development' | 'staging' | 'production';
  features: {
    analytics: boolean;
    errorTracking: boolean;
    debugMode: boolean;
  };
  endpoints: {
    api: string;
    auth: string;
    cdn: string;
  };
}

const development: AppConfig = {
  apiUrl: 'http://localhost:4000',
  environment: 'development',
  features: {
    analytics: false,
    errorTracking: false,
    debugMode: true
  },
  endpoints: {
    api: 'http://localhost:4000/api',
    auth: 'http://localhost:4000/auth',
    cdn: 'http://localhost:3000'
  }
};

const staging: AppConfig = {
  apiUrl: 'https://api-staging.example.com',
  environment: 'staging',
  features: {
    analytics: true,
    errorTracking: true,
    debugMode: true
  },
  endpoints: {
    api: 'https://api-staging.example.com/api',
    auth: 'https://api-staging.example.com/auth',
    cdn: 'https://cdn-staging.example.com'
  }
};

const production: AppConfig = {
  apiUrl: 'https://api.example.com',
  environment: 'production',
  features: {
    analytics: true,
    errorTracking: true,
    debugMode: false
  },
  endpoints: {
    api: 'https://api.example.com/api',
    auth: 'https://api.example.com/auth',
    cdn: 'https://cdn.example.com'
  }
};

const configs = { development, staging, production };

export const config = configs[process.env.NODE_ENV || 'development'];

// Feature flags
export const isProduction = config.environment === 'production';
export const isDevelopment = config.environment === 'development';
export const isStaging = config.environment === 'staging';

// Usage
import { config, isProduction } from '@/config/environments';

if (isProduction) {
  initAnalytics(config.endpoints.api);
}

Example: AWS Secrets Manager integration

// Installation
npm install @aws-sdk/client-secrets-manager

// lib/secrets.ts
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';

const client = new SecretsManagerClient({
  region: process.env.AWS_REGION || 'us-east-1'
});

export async function getSecret(secretName: string): Promise<any> {
  try {
    const command = new GetSecretValueCommand({
      SecretId: secretName
    });
    
    const response = await client.send(command);
    
    if (response.SecretString) {
      return JSON.parse(response.SecretString);
    }
    
    // Binary secret
    const buff = Buffer.from(response.SecretBinary as Uint8Array);
    return buff.toString('ascii');
  } catch (error) {
    console.error('Error retrieving secret:', error);
    throw error;
  }
}

// Cache secrets in memory
const secretsCache = new Map<string, any>();

export async function getCachedSecret(secretName: string): Promise<any> {
  if (secretsCache.has(secretName)) {
    return secretsCache.get(secretName);
  }
  
  const secret = await getSecret(secretName);
  secretsCache.set(secretName, secret);
  
  // Refresh after 1 hour
  setTimeout(() => {
    secretsCache.delete(secretName);
  }, 60 * 60 * 1000);
  
  return secret;
}

// Usage in Next.js API route
export async function GET() {
  const secrets = await getCachedSecret('prod/api/keys');
  
  const response = await fetch(secrets.API_URL, {
    headers: {
      'Authorization': `Bearer ${secrets.API_KEY}`
    }
  });
  
  return Response.json(await response.json());
}

// Terraform to create secret
resource "aws_secretsmanager_secret" "api_keys" {
  name = "prod/api/keys"
  description = "API keys for production"
}

resource "aws_secretsmanager_secret_version" "api_keys" {
  secret_id = aws_secretsmanager_secret.api_keys.id
  secret_string = jsonencode({
    API_URL = "https://api.example.com"
    API_KEY = "secret-key-value"
    STRIPE_KEY = "sk_live_xxx"
  })
}
Security Best Practices: Never commit secrets to git. Use .env.example for documentation. Rotate secrets regularly. Use different secrets per environment. Prefix public variables with NEXT_PUBLIC_ or VITE_. Validate all env vars.

20.5 Blue-Green Canary Deployment

Implement zero-downtime deployment strategies with instant rollback capabilities and gradual traffic shifting.

Strategy Approach Risk Rollback Time
Blue-Green Switch all traffic instantly Medium Instant
Canary Gradual traffic shift (5%→50%→100%) Low Quick
Rolling Update Replace instances one by one Low Minutes
A/B Testing Split traffic by user segment Low N/A (experimental)

Example: Kubernetes Blue-Green deployment

# blue-deployment.yaml (current production)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-blue
  labels:
    app: frontend
    version: blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frontend
      version: blue
  template:
    metadata:
      labels:
        app: frontend
        version: blue
    spec:
      containers:
      - name: frontend
        image: myapp/frontend:v1.0.0
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"

---
# green-deployment.yaml (new version)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-green
  labels:
    app: frontend
    version: green
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frontend
      version: green
  template:
    metadata:
      labels:
        app: frontend
        version: green
    spec:
      containers:
      - name: frontend
        image: myapp/frontend:v2.0.0  # New version
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"

---
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  selector:
    app: frontend
    version: blue  # Initially pointing to blue
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: LoadBalancer

# Deployment process:
# 1. Deploy green version
kubectl apply -f green-deployment.yaml

# 2. Test green version internally
kubectl port-forward deployment/frontend-green 8080:80

# 3. Switch traffic to green (instant cutover)
kubectl patch service frontend-service -p '{"spec":{"selector":{"version":"green"}}}'

# 4. Monitor for issues
kubectl logs -f deployment/frontend-green

# 5. If issues, rollback to blue instantly
kubectl patch service frontend-service -p '{"spec":{"selector":{"version":"blue"}}}'

# 6. If successful, delete blue deployment
kubectl delete deployment frontend-blue

Example: Canary deployment with traffic splitting

# Using Istio for canary deployment

# virtual-service.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: frontend
spec:
  hosts:
  - frontend.example.com
  http:
  - match:
    - headers:
        canary:
          exact: "true"
    route:
    - destination:
        host: frontend
        subset: v2
  - route:
    - destination:
        host: frontend
        subset: v1
      weight: 95
    - destination:
        host: frontend
        subset: v2
      weight: 5  # 5% traffic to canary

---
# destination-rule.yaml
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: frontend
spec:
  host: frontend
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

# Canary deployment script
#!/bin/bash

# Stage 1: Deploy v2 with 5% traffic
kubectl apply -f canary-deployment.yaml
kubectl apply -f virtual-service-5.yaml

# Wait and monitor
sleep 300

# Stage 2: Increase to 25%
kubectl apply -f virtual-service-25.yaml
sleep 300

# Stage 3: Increase to 50%
kubectl apply -f virtual-service-50.yaml
sleep 300

# Stage 4: Increase to 100%
kubectl apply -f virtual-service-100.yaml

# Remove old version
kubectl delete deployment frontend-v1

# Using Flagger for automated canary
# flagger-canary.yaml
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: frontend
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: frontend
  service:
    port: 80
  analysis:
    interval: 1m
    threshold: 10
    maxWeight: 50
    stepWeight: 5
    metrics:
    - name: request-success-rate
      thresholdRange:
        min: 99
      interval: 1m
    - name: request-duration
      thresholdRange:
        max: 500
      interval: 1m
  webhooks:
  - name: load-test
    url: http://flagger-loadtester/
    metadata:
      cmd: "hey -z 1m -q 10 -c 2 http://frontend-canary/"

Example: Vercel deployment with preview URLs

// Vercel automatic preview deployments
// Every PR gets a unique preview URL

// .github/workflows/preview.yml
name: Preview Deployment

on:
  pull_request:
    branches: [main]

jobs:
  deploy-preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v20
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.ORG_ID }}
          vercel-project-id: ${{ secrets.PROJECT_ID }}
          scope: ${{ secrets.VERCEL_SCOPE }}
      
      - name: Comment PR
        uses: actions/github-script@v6
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '🚀 Preview deployed to: https://my-app-pr-${{ github.event.number }}.vercel.app'
            })

// Netlify branch deploys
// netlify.toml
[context.deploy-preview]
  command = "npm run build"
  environment = { NODE_ENV = "staging" }

[context.branch-deploy]
  command = "npm run build"

# Each branch gets: https://branch-name--my-app.netlify.app
# Each PR gets: https://deploy-preview-123--my-app.netlify.app

// Progressive rollout with feature flags
import { useFeatureFlag } from '@/lib/feature-flags';

function NewFeature() {
  const isEnabled = useFeatureFlag('new-ui-redesign', {
    rollout: 10 // Start with 10% of users
  });
  
  if (!isEnabled) {
    return <OldFeature />;
  }
  
  return <RedesignedFeature />;
}

// Gradually increase rollout
// Day 1: 10%
// Day 3: 25%
// Day 7: 50%
// Day 14: 100%
Deployment Strategy Selection: Blue-Green for instant rollback with no downtime. Canary for gradual rollout with monitoring. Rolling update for resource-constrained environments. Always test in staging first.

20.6 Monitoring Sentry Performance Tracking

Implement comprehensive monitoring and error tracking to detect issues early and maintain application health in production.

Tool Purpose Key Features Pricing
Sentry Error tracking, performance Source maps, releases, alerts Free + paid tiers
Datadog Full observability APM, logs, metrics, traces $15+/host/month
New Relic Application monitoring Real user monitoring (RUM) Free + paid
LogRocket Session replay Video replay, console logs $99+/month
Google Analytics 4 User analytics Events, conversions, funnels Free

Example: Sentry integration with source maps

// Installation
npm install @sentry/nextjs

// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  environment: process.env.NODE_ENV,
  
  // Performance Monitoring
  tracesSampleRate: 1.0,
  
  // Session Replay
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
  
  integrations: [
    new Sentry.BrowserTracing({
      tracePropagationTargets: [
        'localhost',
        /^https:\/\/api\.example\.com/
      ]
    }),
    new Sentry.Replay({
      maskAllText: true,
      blockAllMedia: true
    })
  ],
  
  // Filter sensitive data
  beforeSend(event, hint) {
    // Remove PII
    if (event.request) {
      delete event.request.cookies;
      delete event.request.headers;
    }
    
    // Ignore certain errors
    if (event.exception) {
      const error = hint.originalException;
      if (error && error.message?.includes('ResizeObserver')) {
        return null; // Don't send this error
      }
    }
    
    return event;
  }
});

// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 1.0
});

// next.config.js - Source maps
const { withSentryConfig } = require('@sentry/nextjs');

module.exports = withSentryConfig(
  {
    // Your Next.js config
  },
  {
    silent: true,
    org: 'my-org',
    project: 'my-project',
    
    // Upload source maps
    authToken: process.env.SENTRY_AUTH_TOKEN,
    
    // Release tracking
    release: process.env.VERCEL_GIT_COMMIT_SHA,
    
    // Source map options
    widenClientFileUpload: true,
    hideSourceMaps: true,
    disableLogger: true
  }
);

// Usage in components
import * as Sentry from '@sentry/nextjs';

function MyComponent() {
  const handleError = () => {
    try {
      throw new Error('Something went wrong');
    } catch (error) {
      Sentry.captureException(error, {
        tags: {
          component: 'MyComponent',
          action: 'button-click'
        },
        extra: {
          userId: user.id,
          timestamp: Date.now()
        }
      });
    }
  };
  
  return <button onClick={handleError}>Trigger Error</button>;
}

Example: Custom performance monitoring

// lib/monitoring.ts
import * as Sentry from '@sentry/nextjs';

// Track custom metrics
export function trackMetric(name: string, value: number, tags?: Record<string, string>) {
  Sentry.metrics.gauge(name, value, {
    tags,
    unit: 'millisecond'
  });
}

// Track API performance
export async function monitoredFetch(url: string, options?: RequestInit) {
  const startTime = performance.now();
  const transaction = Sentry.startTransaction({
    name: `API ${options?.method || 'GET'} ${url}`,
    op: 'http.client'
  });
  
  try {
    const response = await fetch(url, options);
    const duration = performance.now() - startTime;
    
    // Track success
    trackMetric('api.response_time', duration, {
      endpoint: url,
      status: String(response.status),
      method: options?.method || 'GET'
    });
    
    if (!response.ok) {
      Sentry.captureMessage(`API Error: ${response.status} ${url}`, 'warning');
    }
    
    transaction.setHttpStatus(response.status);
    transaction.finish();
    
    return response;
  } catch (error) {
    const duration = performance.now() - startTime;
    
    // Track failure
    trackMetric('api.error_count', 1, {
      endpoint: url,
      error: error.message
    });
    
    Sentry.captureException(error, {
      tags: { endpoint: url },
      extra: { duration }
    });
    
    transaction.setStatus('internal_error');
    transaction.finish();
    
    throw error;
  }
}

// React Error Boundary with Sentry
import { ErrorBoundary } from '@sentry/nextjs';

function App() {
  return (
    <ErrorBoundary
      fallback={({ error, resetError }) => (
        <div>
          <h1>Something went wrong</h1>
          <p>{error.message}</p>
          <button onClick={resetError}>Try again</button>
        </div>
      )}
      showDialog
    >
      <YourApp />
    </ErrorBoundary>
  );
}

// Core Web Vitals tracking
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

function sendToAnalytics(metric) {
  // Send to Sentry
  Sentry.metrics.gauge(metric.name, metric.value, {
    tags: {
      rating: metric.rating
    },
    unit: metric.name === 'CLS' ? 'ratio' : 'millisecond'
  });
  
  // Send to Google Analytics
  gtag('event', metric.name, {
    value: Math.round(metric.value),
    metric_id: metric.id,
    metric_rating: metric.rating
  });
}

getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);

Example: Comprehensive monitoring dashboard setup

// Datadog RUM integration
import { datadogRum } from '@datadog/browser-rum';

datadogRum.init({
  applicationId: process.env.NEXT_PUBLIC_DD_APP_ID!,
  clientToken: process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN!,
  site: 'datadoghq.com',
  service: 'my-app',
  env: process.env.NODE_ENV,
  version: process.env.NEXT_PUBLIC_APP_VERSION,
  
  sessionSampleRate: 100,
  sessionReplaySampleRate: 20,
  trackUserInteractions: true,
  trackResources: true,
  trackLongTasks: true,
  
  defaultPrivacyLevel: 'mask-user-input'
});

// Custom monitoring service
class MonitoringService {
  private static instance: MonitoringService;
  
  private constructor() {
    this.initHealthChecks();
  }
  
  static getInstance() {
    if (!MonitoringService.instance) {
      MonitoringService.instance = new MonitoringService();
    }
    return MonitoringService.instance;
  }
  
  // Track page views
  trackPageView(page: string) {
    gtag('event', 'page_view', { page_path: page });
    Sentry.addBreadcrumb({
      category: 'navigation',
      message: `Navigated to ${page}`,
      level: 'info'
    });
  }
  
  // Track custom events
  trackEvent(name: string, properties?: Record<string, any>) {
    gtag('event', name, properties);
    Sentry.addBreadcrumb({
      category: 'user-action',
      message: name,
      data: properties,
      level: 'info'
    });
  }
  
  // Track errors
  trackError(error: Error, context?: Record<string, any>) {
    console.error(error);
    Sentry.captureException(error, {
      extra: context
    });
  }
  
  // Health checks
  private initHealthChecks() {
    setInterval(() => {
      this.checkApiHealth();
      this.checkPerformance();
    }, 60000); // Every minute
  }
  
  private async checkApiHealth() {
    try {
      const response = await fetch('/api/health');
      if (!response.ok) {
        this.trackError(new Error('API health check failed'), {
          status: response.status
        });
      }
    } catch (error) {
      this.trackError(error as Error, { check: 'api-health' });
    }
  }
  
  private checkPerformance() {
    const memory = (performance as any).memory;
    if (memory && memory.usedJSHeapSize > 100 * 1024 * 1024) {
      Sentry.captureMessage('High memory usage detected', {
        level: 'warning',
        extra: {
          usedHeapSize: memory.usedJSHeapSize,
          totalHeapSize: memory.totalJSHeapSize
        }
      });
    }
  }
}

export const monitoring = MonitoringService.getInstance();

// Usage
monitoring.trackEvent('button_click', { button: 'checkout' });
monitoring.trackError(new Error('Payment failed'), { amount: 99.99 });

Production Deployment Checklist

Category Task Priority Status
Hosting Configure CDN and caching High
Security Set up HTTPS and security headers Critical
Monitoring Integrate error tracking (Sentry) Critical
Performance Track Core Web Vitals High
CI/CD Automated deployment pipeline High
Config Environment variables properly set Critical
Deployment Blue-green or canary strategy Medium
Backup Rollback plan documented High