React Ecosystem and Popular Libraries

1. React Router and Client-side Navigation (v6)

Feature API Example Use Case
Basic Routing BrowserRouter, Routes, Route <Route path="/about" element={<About />} /> Define app routes and components
Dynamic Routes :param syntax, useParams() <Route path="/user/:id" /> Routes with variable segments
Navigation Link, NavLink, useNavigate() <Link to="/about">About</Link> Navigate between routes
Nested Routes Outlet, nested Route <Route path="dashboard" element={<Layout />}> Layout with child routes
Protected Routes Navigate, conditional rendering {!auth ? <Navigate to="/login" /> : children} Restrict access by auth status
Query Params useSearchParams() const [params, setParams] = useSearchParams() Read/write URL query strings
Lazy Loading React.lazy(), Suspense const About = lazy(() => import('./About')) Code-split route components

Example: React Router v6 setup

// Install
npm install react-router-dom

// App.tsx
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/users">Users</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/users" element={<Users />} />
        <Route path="/users/:id" element={<UserProfile />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

// Dynamic route with useParams
function UserProfile() {
  const { id } = useParams();
  return <div>User Profile: {id}</div>;
}

// Programmatic navigation
function LoginButton() {
  const navigate = useNavigate();
  
  const handleLogin = async () => {
    await login();
    navigate('/dashboard', { replace: true });
  };
  
  return <button onClick={handleLogin}>Login</button>;
}

Example: Nested routes and layouts

import { Outlet } from 'react-router-dom';

// Layout component with Outlet
function DashboardLayout() {
  return (
    <div>
      <header>Dashboard Header</header>
      <aside>Sidebar</aside>
      <main>
        <Outlet /> {/* Child routes render here */}
      </main>
    </div>
  );
}

// App with nested routes
function App() {
  return (
    <Routes>
      <Route path="/dashboard" element={<DashboardLayout />}>
        <Route index element={<DashboardHome />} />
        <Route path="profile" element={<Profile />} />
        <Route path="settings" element={<Settings />} />
      </Route>
    </Routes>
  );
}

// Protected route wrapper
function ProtectedRoute({ children }) {
  const { isAuthenticated } = useAuth();
  
  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }
  
  return children;
}

// Usage
<Route 
  path="/dashboard" 
  element={
    <ProtectedRoute>
      <DashboardLayout />
    </ProtectedRoute>
  }
/>

2. State Management Libraries (Redux, Zustand, Jotai, Recoil)

Library Key Features Core API Best For
Redux Toolkit Reducers, actions, middleware, DevTools configureStore, createSlice, createAsyncThunk Large apps, complex state, predictable updates
Zustand Minimal, no boilerplate, React-first create(), set(), get() Simple global state, quick setup, small apps
Jotai Atomic state, bottom-up approach atom(), useAtom() Fine-grained reactivity, derived state
Recoil Atoms, selectors, async state atom(), selector(), useRecoilState() Complex derived state, async dependencies
MobX Observable state, automatic tracking makeObservable, observer, action OOP patterns, automatic reactivity
Valtio Proxy-based, mutable syntax proxy(), useSnapshot() Mutable-style state, simple API

Example: Redux Toolkit setup

// Install
npm install @reduxjs/toolkit react-redux

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

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

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

interface CounterState {
  value: number;
}

const initialState: CounterState = { value: 0 };

const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1; // Immer makes this safe
    },
    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;

// App.tsx
import { Provider } from 'react-redux';
import { store } from './store';

function App() {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  );
}

// Counter.tsx
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './counterSlice';

function Counter() {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();
  
  return (
    <div>
      <button onClick={() => dispatch(decrement())}>-</button>
      <span>{count}</span>
      <button onClick={() => dispatch(increment())}>+</button>
    </div>
  );
}

Example: Zustand - minimal state management

// Install
npm install zustand

// store.ts
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';

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

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

// Component.tsx
function BearCounter() {
  const bears = useBearStore((state) => state.bears);
  return <h1>{bears} bears</h1>;
}

function Controls() {
  const increase = useBearStore((state) => state.increase);
  const decrease = useBearStore((state) => state.decrease);
  
  return (
    <div>
      <button onClick={decrease}>-</button>
      <button onClick={increase}>+</button>
    </div>
  );
}

// Async actions
interface UserStore {
  user: User | null;
  fetchUser: (id: string) => Promise<void>;
}

export const useUserStore = create<UserStore>((set) => ({
  user: null,
  fetchUser: async (id) => {
    const response = await fetch(`/api/users/${id}`);
    const user = await response.json();
    set({ user });
  },
}));

3. UI Component Libraries (MUI, Chakra UI, shadcn/ui)

Library Design System Key Features Bundle Size
Material-UI (MUI) Material Design Comprehensive components, theming, customization, TypeScript Large (~300KB min+gzip)
Ant Design Enterprise UI 200+ components, i18n, form validation, charts Large (~500KB min+gzip)
Chakra UI Accessible, composable Style props, dark mode, responsive, a11y-first Medium (~150KB min+gzip)
Mantine Modern, feature-rich 120+ components, hooks, form management, notifications Medium (~180KB min+gzip)
shadcn/ui Copy-paste components Radix UI + Tailwind, full control, no package dependency Small (only what you use)
Radix UI Headless components Unstyled, accessible primitives, full control Small (~50KB per component)
Headless UI Tailwind Labs Unstyled, accessible, works with Tailwind Small (~20KB min+gzip)

Example: Material-UI (MUI) setup

// Install
npm install @mui/material @emotion/react @emotion/styled

// App.tsx
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { Button, Container, Typography, Box } from '@mui/material';
import CssBaseline from '@mui/material/CssBaseline';

const theme = createTheme({
  palette: {
    primary: {
      main: '#1976d2',
    },
    secondary: {
      main: '#dc004e',
    },
  },
});

function App() {
  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <Container>
        <Typography variant="h1" component="h1" gutterBottom>
          Hello MUI
        </Typography>
        <Box sx={{ display: 'flex', gap: 2 }}>
          <Button variant="contained" color="primary">
            Primary
          </Button>
          <Button variant="outlined" color="secondary">
            Secondary
          </Button>
        </Box>
      </Container>
    </ThemeProvider>
  );
}

Example: Chakra UI setup

// Install
npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion

// App.tsx
import { ChakraProvider, Box, Button, Heading, VStack } from '@chakra-ui/react';
import { extendTheme } from '@chakra-ui/react';

const theme = extendTheme({
  colors: {
    brand: {
      50: '#e3f2fd',
      500: '#2196f3',
      900: '#0d47a1',
    },
  },
});

function App() {
  return (
    <ChakraProvider theme={theme}>
      <Box p={8}>
        <VStack spacing={4} align="stretch">
          <Heading size="xl">Hello Chakra</Heading>
          <Button colorScheme="brand" size="lg">
            Click Me
          </Button>
          <Box bg="gray.100" p={4} borderRadius="md">
            Responsive Box
          </Box>
        </VStack>
      </Box>
    </ChakraProvider>
  );
}

Example: shadcn/ui - copy-paste approach

// Install CLI
npx shadcn-ui@latest init

// Add components
npx shadcn-ui@latest add button
npx shadcn-ui@latest add card

// Components are copied to your project
// components/ui/button.tsx
// components/ui/card.tsx

// Usage
import { Button } from '@/components/ui/button';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';

function App() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>Welcome</CardTitle>
      </CardHeader>
      <CardContent>
        <Button variant="default">Click Me</Button>
        <Button variant="outline">Outline</Button>
      </CardContent>
    </Card>
  );
}

// Full control - edit copied components directly
// Tailwind-based styling
// No package lock-in

4. CSS-in-JS Libraries (styled-components, Emotion, CSS Modules)

Solution Approach Features Performance
styled-components Tagged templates Automatic vendor prefixing, theming, SSR, TypeScript Runtime CSS generation
Emotion Tagged templates or object Fast, flexible, source maps, framework agnostic Runtime, faster than styled-components
Tailwind CSS Utility classes JIT compiler, purging, plugins, mobile-first Zero runtime, build-time
CSS Modules Scoped CSS files Local scope, composition, type-safe with TypeScript Zero runtime, standard CSS
Vanilla Extract Zero-runtime CSS-in-TS Type-safe, theme contracts, build-time extraction Zero runtime, static CSS
Linaria Zero-runtime CSS-in-JS Build-time extraction, critical CSS, SSR Zero runtime, extracted CSS
Panda CSS Build-time atomic CSS Type-safe, recipes, variants, zero runtime Zero runtime, optimized output

Example: styled-components

// Install
npm install styled-components

// Component.tsx
import styled from 'styled-components';

const Button = styled.button`
  background: ${props => props.primary ? '#007bff' : '#6c757d'};
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  
  &:hover {
    opacity: 0.8;
  }
  
  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
`;

const Container = styled.div`
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
  
  @media (max-width: 768px) {
    padding: 10px;
  }
`;

// Usage
function App() {
  return (
    <Container>
      <Button primary>Primary</Button>
      <Button>Secondary</Button>
    </Container>
  );
}

// Theming
import { ThemeProvider } from 'styled-components';

const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
  },
  spacing: {
    small: '8px',
    medium: '16px',
  },
};

const ThemedButton = styled.button`
  background: ${props => props.theme.colors.primary};
  padding: ${props => props.theme.spacing.medium};
`;

function App() {
  return (
    <ThemeProvider theme={theme}>
      <ThemedButton>Themed</ThemedButton>
    </ThemeProvider>
  );
}

Example: Tailwind CSS with React

// Install
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

// tailwind.config.js
module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    extend: {
      colors: {
        brand: '#007bff',
      },
    },
  },
  plugins: [],
};

// index.css
@tailwind base;
@tailwind components;
@tailwind utilities;

// Component.tsx
function Button({ children, variant = 'primary' }) {
  const baseClasses = 'px-4 py-2 rounded font-medium transition';
  const variantClasses = {
    primary: 'bg-blue-500 text-white hover:bg-blue-600',
    secondary: 'bg-gray-500 text-white hover:bg-gray-600',
    outline: 'border border-blue-500 text-blue-500 hover:bg-blue-50',
  };
  
  return (
    <button className={`${baseClasses} ${variantClasses[variant]}`}>
      {children}
    </button>
  );
}

// With clsx for conditional classes
import clsx from 'clsx';

function Card({ children, active }) {
  return (
    <div className={clsx(
      'p-4 rounded-lg shadow',
      active ? 'bg-blue-100 border-blue-500' : 'bg-white border-gray-200',
      'border-2 transition-colors'
    )}>
      {children}
    </div>
  );
}

Example: CSS Modules

// Button.module.css
.button {
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: opacity 0.2s;
}

.button:hover {
  opacity: 0.8;
}

.primary {
  background: #007bff;
  color: white;
}

.secondary {
  background: #6c757d;
  color: white;
}

.button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

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

interface ButtonProps {
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
  children: React.ReactNode;
}

function Button({ variant = 'primary', disabled, children }: ButtonProps) {
  return (
    <button 
      className={clsx(styles.button, styles[variant])}
      disabled={disabled}
    >
      {children}
    </button>
  );
}

// Composition
.base {
  padding: 10px;
}

.large {
  composes: base;
  padding: 20px;
  font-size: 18px;
}

5. Form Libraries (Formik, React Hook Form, Zod)

Library Key Features Validation Best For
React Hook Form Performance-focused, minimal re-renders, small bundle Built-in, Yup, Zod, Joi integration Complex forms, performance-critical apps
Formik Popular, full-featured, field-level validation Built-in, Yup integration Standard forms, familiar API
Final Form Framework-agnostic, subscription-based updates Custom validators, async validation Flexible form state management
Zod TypeScript-first schema validation Type inference, parse, transform TypeScript projects, type-safe forms
Yup Schema-based validation, chainable API Sync/async validation, custom rules Form validation, data validation
TanStack Form Framework-agnostic, type-safe, adapters Custom validators, async, field-level Modern forms, framework flexibility

Example: React Hook Form with Zod

// Install
npm install react-hook-form @hookform/resolvers zod

// LoginForm.tsx
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

// Define schema
const loginSchema = z.object({
  email: z.string().email('Invalid email address'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
  rememberMe: z.boolean().optional(),
});

type LoginFormData = z.infer<typeof loginSchema>;

function LoginForm() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<LoginFormData>({
    resolver: zodResolver(loginSchema),
  });

  const onSubmit = async (data: LoginFormData) => {
    await loginUser(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label htmlFor="email">Email</label>
        <input 
          {...register('email')} 
          type="email" 
          id="email"
        />
        {errors.email && <span>{errors.email.message}</span>}
      </div>

      <div>
        <label htmlFor="password">Password</label>
        <input 
          {...register('password')} 
          type="password" 
          id="password"
        />
        {errors.password && <span>{errors.password.message}</span>}
      </div>

      <div>
        <label>
          <input {...register('rememberMe')} type="checkbox" />
          Remember Me
        </label>
      </div>

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Logging in...' : 'Login'}
      </button>
    </form>
  );
}

Example: Dynamic fields and arrays

import { useForm, useFieldArray } from 'react-hook-form';

const schema = z.object({
  name: z.string().min(1),
  emails: z.array(
    z.object({
      value: z.string().email(),
      primary: z.boolean(),
    })
  ).min(1, 'At least one email required'),
});

function ProfileForm() {
  const { register, control, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema),
    defaultValues: {
      name: '',
      emails: [{ value: '', primary: true }],
    },
  });

  const { fields, append, remove } = useFieldArray({
    control,
    name: 'emails',
  });

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('name')} placeholder="Name" />
      {errors.name && <span>{errors.name.message}</span>}

      {fields.map((field, index) => (
        <div key={field.id}>
          <input
            {...register(`emails.${index}.value`)}
            placeholder="Email"
          />
          <label>
            <input
              {...register(`emails.${index}.primary`)}
              type="checkbox"
            />
            Primary
          </label>
          <button type="button" onClick={() => remove(index)}>
            Remove
          </button>
        </div>
      ))}

      <button type="button" onClick={() => append({ value: '', primary: false })}>
        Add Email
      </button>

      <button type="submit">Submit</button>
    </form>
  );
}

6. Data Fetching Libraries (SWR, React Query, TanStack Query)

Library Features Key Benefits Use Case
TanStack Query (React Query) Caching, refetching, pagination, infinite scroll, mutations Automatic background updates, optimistic updates, devtools Complex data fetching, real-time updates
SWR Stale-while-revalidate, focus revalidation, lightweight Simple API, automatic revalidation, fast Simple data fetching, real-time apps
Apollo Client GraphQL-focused, caching, subscriptions, local state GraphQL integration, normalized cache, tooling GraphQL APIs, complex data graphs
RTK Query Redux Toolkit integration, auto-generated hooks Redux integration, code generation, TypeScript Redux apps, REST/GraphQL APIs
tRPC End-to-end type safety, React Query integration No code generation, type inference, full-stack TypeScript full-stack apps

Example: React Query (TanStack Query)

// Install
npm install @tanstack/react-query

// Setup
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60 * 1000, // 1 minute
      refetchOnWindowFocus: false,
    },
  },
});

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

// Basic query
import { useQuery } from '@tanstack/react-query';

function UserProfile({ userId }) {
  const { data, isLoading, error } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
  });

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

  return <div>{data.name}</div>;
}

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

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

  const mutation = useMutation({
    mutationFn: createPost,
    onSuccess: () => {
      // Invalidate and refetch
      queryClient.invalidateQueries({ queryKey: ['posts'] });
    },
  });

  return (
    <button onClick={() => mutation.mutate({ title: 'New Post' })}>
      Create Post
    </button>
  );
}

// Optimistic updates
const mutation = useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos'] });
    const previousTodos = queryClient.getQueryData(['todos']);
    
    queryClient.setQueryData(['todos'], (old) => [...old, newTodo]);
    
    return { previousTodos };
  },
  onError: (err, newTodo, context) => {
    queryClient.setQueryData(['todos'], context.previousTodos);
  },
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] });
  },
});

Example: SWR - simple data fetching

// Install
npm install swr

// Basic usage
import useSWR from 'swr';

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

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher);

  if (error) return <div>Failed to load</div>;
  if (isLoading) return <div>Loading...</div>;

  return <div>Hello {data.name}!</div>;
}

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

function App() {
  return (
    <SWRConfig 
      value={{
        fetcher: (url) => fetch(url).then(res => res.json()),
        refreshInterval: 3000,
        revalidateOnFocus: true,
      }}
    >
      <Dashboard />
    </SWRConfig>
  );
}

// Mutations
import useSWRMutation from 'swr/mutation';

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

function Settings() {
  const { trigger, isMutating } = useSWRMutation('/api/user', updateUser);

  return (
    <button 
      disabled={isMutating}
      onClick={() => trigger({ name: 'New Name' })}
    >
      Update User
    </button>
  );
}

// Pagination
function Projects() {
  const [pageIndex, setPageIndex] = useState(0);
  
  const { data } = useSWR(`/api/projects?page=${pageIndex}`, fetcher);

  return (
    <div>
      {data?.projects.map(p => <div key={p.id}>{p.name}</div>)}
      <button onClick={() => setPageIndex(pageIndex - 1)}>Previous</button>
      <button onClick={() => setPageIndex(pageIndex + 1)}>Next</button>
    </div>
  );
}

Example: Infinite scroll with React Query

import { useInfiniteQuery } from '@tanstack/react-query';
import { useInView } from 'react-intersection-observer';

function Posts() {
  const { ref, inView } = useInView();

  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery({
    queryKey: ['posts'],
    queryFn: ({ pageParam = 0 }) => fetchPosts(pageParam),
    getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
  });

  useEffect(() => {
    if (inView && hasNextPage) {
      fetchNextPage();
    }
  }, [inView, hasNextPage, fetchNextPage]);

  return (
    <div>
      {data?.pages.map((page) => (
        <div key={page.nextCursor}>
          {page.posts.map((post) => (
            <div key={post.id}>{post.title}</div>
          ))}
        </div>
      ))}
      
      <div ref={ref}>
        {isFetchingNextPage ? 'Loading more...' : hasNextPage ? 'Load More' : 'Nothing more'}
      </div>
    </div>
  );
}
Ecosystem Integration Tips: Choose libraries based on project needs, bundle size matters, prefer TypeScript-friendly libraries, check maintenance and community, test library compatibility, use tree-shaking when available, consider migration path, read documentation thoroughly, start simple then add complexity, benchmark performance in your app.

React Ecosystem Integration Summary