> ## Documentation Index
> Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 08. Context API

> Master global state management, avoid prop drilling, and implement advanced Context patterns.

# Context API

Context provides a way to share data across the component tree without manually passing props at every level. It's React's built-in solution for "global" state.

**Real-world analogy**: Imagine a building where the Wi-Fi password is posted in the lobby (the Provider). Any room (component) in the building can check the lobby sign directly -- nobody needs to relay the password door-to-door from room to room. That relay process is prop drilling, and Context is the lobby sign that makes it unnecessary.

## The Problem: Prop Drilling

Without Context, passing data to deeply nested components requires threading props through every intermediate component -- even the ones that do not use the data themselves:

```
┌─────────────────────────────────────────────────────────────┐
│                          App                                │
│                      theme="dark"                           │
│                           │                                 │
│                           ▼                                 │
│                        Layout                               │
│                      theme={theme}   ← Doesn't use it      │
│                           │                                 │
│                           ▼                                 │
│                       Sidebar                               │
│                      theme={theme}   ← Doesn't use it      │
│                           │                                 │
│                           ▼                                 │
│                        Menu                                 │
│                      theme={theme}   ← Doesn't use it      │
│                           │                                 │
│                           ▼                                 │
│                      MenuItem                               │
│                      theme={theme}   ← Finally uses it!    │
└─────────────────────────────────────────────────────────────┘
```

With Context, components can access shared data directly:

```
┌─────────────────────────────────────────────────────────────┐
│                   ThemeContext.Provider                     │
│                      value="dark"                           │
│                           │                                 │
│                 ┌─────────┴─────────┐                      │
│                 ▼                   ▼                       │
│              Layout              Sidebar                    │
│                 │                   │                       │
│                 ▼                   ▼                       │
│              Header              MenuItem                   │
│           useContext()        useContext()                  │
│               ↑                    ↑                        │
│               └────────────────────┘                        │
│               Direct access to "dark"                       │
└─────────────────────────────────────────────────────────────┘
```

***

## Creating and Using Context

### Step 1: Create the Context

```javascript theme={null}
// ThemeContext.js
import { createContext } from 'react';

// Default value (used when no Provider is found)
export const ThemeContext = createContext('light');
```

### Step 2: Provide the Context

```javascript theme={null}
// App.jsx
import { ThemeContext } from './ThemeContext';

function App() {
  const [theme, setTheme] = useState('dark');

  return (
    <ThemeContext.Provider value={theme}>
      <Layout />
    </ThemeContext.Provider>
  );
}
```

### Step 3: Consume the Context

```javascript theme={null}
// MenuItem.jsx
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

function MenuItem({ label }) {
  const theme = useContext(ThemeContext);
  
  return (
    <li className={`menu-item ${theme}`}>
      {label}
    </li>
  );
}
```

***

## Complete Example: Theme Context

Here's a full implementation with toggling:

```javascript theme={null}
// contexts/ThemeContext.jsx
import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext(null);

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };

  const value = {
    theme,
    toggleTheme,
    isDark: theme === 'dark'
  };

  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}
```

```javascript theme={null}
// App.jsx
import { ThemeProvider } from './contexts/ThemeContext';

function App() {
  return (
    <ThemeProvider>
      <div className="app">
        <Header />
        <Main />
      </div>
    </ThemeProvider>
  );
}
```

```javascript theme={null}
// components/ThemeToggle.jsx
import { useTheme } from '../contexts/ThemeContext';

function ThemeToggle() {
  const { theme, toggleTheme, isDark } = useTheme();

  return (
    <button onClick={toggleTheme}>
      {isDark ? '☀️ Light Mode' : '🌙 Dark Mode'}
    </button>
  );
}
```

<Tip>
  **Best Practice**: Always create a custom hook (`useTheme`) instead of exporting the context directly. This:

  * Provides better error messages
  * Makes refactoring easier
  * Hides implementation details
</Tip>

***

## Real-World Pattern: Auth Context

```javascript theme={null}
// contexts/AuthContext.jsx
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 for existing session
    const checkAuth = async () => {
      try {
        const token = localStorage.getItem('token');
        if (token) {
          const response = await fetch('/api/auth/me', {
            headers: { Authorization: `Bearer ${token}` }
          });
          if (response.ok) {
            const userData = await response.json();
            setUser(userData);
          }
        }
      } catch (error) {
        console.error('Auth check failed:', error);
      } finally {
        setLoading(false);
      }
    };
    
    checkAuth();
  }, []);

  const login = async (email, password) => {
    const response = await fetch('/api/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password })
    });
    
    if (!response.ok) {
      throw new Error('Login failed');
    }
    
    const { user, token } = await response.json();
    localStorage.setItem('token', token);
    setUser(user);
    return user;
  };

  const logout = () => {
    localStorage.removeItem('token');
    setUser(null);
  };

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

  return (
    <AuthContext.Provider value={value}>
      {!loading && children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}
```

### Using Auth Context

```javascript theme={null}
// components/Navbar.jsx
function Navbar() {
  const { user, isAuthenticated, logout } = useAuth();

  return (
    <nav>
      <Logo />
      {isAuthenticated ? (
        <>
          <span>Welcome, {user.name}</span>
          <button onClick={logout}>Logout</button>
        </>
      ) : (
        <Link to="/login">Login</Link>
      )}
    </nav>
  );
}
```

```javascript theme={null}
// pages/LoginPage.jsx
function LoginPage() {
  const { login } = useAuth();
  const navigate = useNavigate();
  const [error, setError] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      await login(email, password);
      navigate('/dashboard');
    } catch (err) {
      setError('Invalid credentials');
    }
  };

  return (/* form */);
}
```

***

## Multiple Contexts

You can compose multiple contexts:

```javascript theme={null}
// App.jsx
function App() {
  return (
    <AuthProvider>
      <ThemeProvider>
        <NotificationProvider>
          <Router>
            <Layout />
          </Router>
        </NotificationProvider>
      </ThemeProvider>
    </AuthProvider>
  );
}
```

### Alternative: Compose Providers

```javascript theme={null}
// contexts/AppProviders.jsx
function AppProviders({ children }) {
  return (
    <AuthProvider>
      <ThemeProvider>
        <NotificationProvider>
          {children}
        </NotificationProvider>
      </ThemeProvider>
    </AuthProvider>
  );
}

// App.jsx
function App() {
  return (
    <AppProviders>
      <Router>
        <Layout />
      </Router>
    </AppProviders>
  );
}
```

***

## Performance Optimization

### The Re-render Problem

This is the biggest gotcha with Context and the reason senior developers sometimes avoid it for frequently-changing state. When context value changes, **ALL** consuming components re-render, even if they only use part of the context. There is no built-in way to subscribe to just a slice of the context value.

```javascript theme={null}
// ❌ Problem: Updating `cart` re-renders ThemeToggle
const AppContext = createContext();

function AppProvider({ children }) {
  const [theme, setTheme] = useState('light');
  const [cart, setCart] = useState([]);
  
  return (
    <AppContext.Provider value={{ theme, setTheme, cart, setCart }}>
      {children}
    </AppContext.Provider>
  );
}

function ThemeToggle() {
  const { theme, setTheme } = useContext(AppContext);
  // Re-renders when cart changes! 😱
}
```

### Solution 1: Split Contexts

```javascript theme={null}
// ✅ Separate concerns into different contexts
const ThemeContext = createContext();
const CartContext = createContext();

function App() {
  return (
    <ThemeProvider>
      <CartProvider>
        <Layout />
      </CartProvider>
    </ThemeProvider>
  );
}

function ThemeToggle() {
  const { theme, setTheme } = useTheme();
  // Only re-renders when theme changes ✅
}
```

### Solution 2: Memoize Context Value

```javascript theme={null}
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  // Memoize the value object
  const value = useMemo(() => ({
    theme,
    toggleTheme: () => setTheme(t => t === 'light' ? 'dark' : 'light')
  }), [theme]);

  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  );
}
```

### Solution 3: State and Dispatch Separation

```javascript theme={null}
const StateContext = createContext();
const DispatchContext = createContext();

function AppProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <StateContext.Provider value={state}>
      <DispatchContext.Provider value={dispatch}>
        {children}
      </DispatchContext.Provider>
    </StateContext.Provider>
  );
}

// Components that only dispatch don't re-render on state changes
function AddButton() {
  const dispatch = useContext(DispatchContext);
  return <button onClick={() => dispatch({ type: 'ADD' })}>Add</button>;
}
```

***

## Context with useReducer

For complex state, combine Context with useReducer:

```javascript theme={null}
// contexts/CartContext.jsx
const CartContext = createContext();

const cartReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_ITEM':
      const existing = state.items.find(i => i.id === action.payload.id);
      if (existing) {
        return {
          ...state,
          items: state.items.map(i =>
            i.id === action.payload.id
              ? { ...i, quantity: i.quantity + 1 }
              : i
          )
        };
      }
      return {
        ...state,
        items: [...state.items, { ...action.payload, quantity: 1 }]
      };
      
    case 'REMOVE_ITEM':
      return {
        ...state,
        items: state.items.filter(i => i.id !== action.payload)
      };
      
    case 'UPDATE_QUANTITY':
      return {
        ...state,
        items: state.items.map(i =>
          i.id === action.payload.id
            ? { ...i, quantity: action.payload.quantity }
            : i
        )
      };
      
    case 'CLEAR_CART':
      return { ...state, items: [] };
      
    default:
      return state;
  }
};

export function CartProvider({ children }) {
  const [state, dispatch] = useReducer(cartReducer, { items: [] });

  const addItem = (product) => dispatch({ type: 'ADD_ITEM', payload: product });
  const removeItem = (id) => dispatch({ type: 'REMOVE_ITEM', payload: id });
  const updateQuantity = (id, quantity) => dispatch({ type: 'UPDATE_QUANTITY', payload: { id, quantity } });
  const clearCart = () => dispatch({ type: 'CLEAR_CART' });

  const total = state.items.reduce(
    (sum, item) => sum + item.price * item.quantity,
    0
  );

  const itemCount = state.items.reduce(
    (sum, item) => sum + item.quantity,
    0
  );

  return (
    <CartContext.Provider value={{
      items: state.items,
      total,
      itemCount,
      addItem,
      removeItem,
      updateQuantity,
      clearCart
    }}>
      {children}
    </CartContext.Provider>
  );
}

export const useCart = () => {
  const context = useContext(CartContext);
  if (!context) {
    throw new Error('useCart must be used within a CartProvider');
  }
  return context;
};
```

***

## Context Pitfalls

<Warning>
  **Pitfall 1 -- Creating a new value object on every render**: If your Provider creates the `value` object inline, every render creates a new reference, and all consumers re-render -- even if the actual data has not changed.

  ```javascript theme={null}
  // BAD: new object on every render
  <ThemeContext.Provider value={{ theme, toggleTheme }}>

  // GOOD: memoize the value
  const value = useMemo(() => ({ theme, toggleTheme }), [theme]);
  <ThemeContext.Provider value={value}>
  ```

  **Pitfall 2 -- Using Context for frequently-changing state**: Context is designed for state that changes infrequently (theme, locale, auth). If you put rapidly-changing state (mouse position, animation frames, typing input) into Context, every consumer re-renders on every change. For high-frequency updates, use `useRef` with a subscription pattern, or a library like Zustand or Jotai that supports fine-grained subscriptions.

  **Pitfall 3 -- Forgetting the Provider**: If a component calls `useContext(MyContext)` but is not wrapped in a `MyContext.Provider`, it silently receives the default value from `createContext()`. If the default is `undefined`, you get cryptic "cannot read property of undefined" errors. Always use a custom hook with an error boundary: `if (!context) throw new Error('useMyContext must be used within a MyProvider')`.

  **Pitfall 4 -- Nesting providers in the wrong order**: If ContextA's Provider needs data from ContextB, ContextB's Provider must be the outer wrapper. Getting the order wrong means ContextA's Provider tries to call `useContext(ContextB)` outside of ContextB's Provider tree and gets the default value.
</Warning>

***

## When to Use Context vs. Other Solutions

| Scenario                        | Recommendation                 |
| ------------------------------- | ------------------------------ |
| Theme, locale, user data        | ✅ Context                      |
| Data needed by 2-3 components   | Props (avoid over-engineering) |
| Complex state with many actions | Context + useReducer or Redux  |
| Server state (API data)         | React Query / SWR              |
| Frequent updates (animations)   | Zustand, Jotai, or refs        |
| Very large apps                 | Redux Toolkit, Zustand         |

***

## 🎯 Practice Exercises

<Accordion title="Exercise 1: Notification Context">
  ```javascript theme={null}
  // contexts/NotificationContext.jsx
  const NotificationContext = createContext();

  export function NotificationProvider({ children }) {
    const [notifications, setNotifications] = useState([]);

    const addNotification = (message, type = 'info') => {
      const id = Date.now();
      setNotifications(prev => [...prev, { id, message, type }]);
      
      // Auto-remove after 5 seconds
      setTimeout(() => {
        removeNotification(id);
      }, 5000);
    };

    const removeNotification = (id) => {
      setNotifications(prev => prev.filter(n => n.id !== id));
    };

    return (
      <NotificationContext.Provider value={{ notifications, addNotification, removeNotification }}>
        {children}
        <NotificationContainer />
      </NotificationContext.Provider>
    );
  }

  function NotificationContainer() {
    const { notifications, removeNotification } = useNotifications();

    return (
      <div className="notification-container">
        {notifications.map(n => (
          <div key={n.id} className={`notification notification-${n.type}`}>
            {n.message}
            <button onClick={() => removeNotification(n.id)}>×</button>
          </div>
        ))}
      </div>
    );
  }

  export const useNotifications = () => useContext(NotificationContext);

  // Usage
  function SaveButton() {
    const { addNotification } = useNotifications();
    
    const handleSave = async () => {
      try {
        await saveData();
        addNotification('Saved successfully!', 'success');
      } catch {
        addNotification('Failed to save', 'error');
      }
    };
    
    return <button onClick={handleSave}>Save</button>;
  }
  ```
</Accordion>

<Accordion title="Exercise 2: Language/i18n Context">
  ```javascript theme={null}
  // contexts/LanguageContext.jsx
  const translations = {
    en: {
      greeting: 'Hello',
      goodbye: 'Goodbye',
      welcome: 'Welcome to our app'
    },
    es: {
      greeting: 'Hola',
      goodbye: 'Adiós',
      welcome: 'Bienvenido a nuestra aplicación'
    },
    fr: {
      greeting: 'Bonjour',
      goodbye: 'Au revoir',
      welcome: 'Bienvenue dans notre application'
    }
  };

  const LanguageContext = createContext();

  export function LanguageProvider({ children }) {
    const [language, setLanguage] = useState('en');

    const t = (key) => translations[language][key] || key;

    return (
      <LanguageContext.Provider value={{ language, setLanguage, t }}>
        {children}
      </LanguageContext.Provider>
    );
  }

  export const useLanguage = () => useContext(LanguageContext);

  // Usage
  function WelcomeBanner() {
    const { t } = useLanguage();
    return <h1>{t('welcome')}</h1>;
  }

  function LanguageSelector() {
    const { language, setLanguage } = useLanguage();
    
    return (
      <select value={language} onChange={e => setLanguage(e.target.value)}>
        <option value="en">English</option>
        <option value="es">Español</option>
        <option value="fr">Français</option>
      </select>
    );
  }
  ```
</Accordion>

***

## Summary

| Concept                  | Description                                      |
| ------------------------ | ------------------------------------------------ |
| **Context**              | Share data without passing props manually        |
| **createContext**        | Creates a context object                         |
| **Provider**             | Component that provides the value                |
| **useContext**           | Hook to consume context                          |
| **Custom Hook**          | Best practice for consuming context              |
| **Prop Drilling**        | Problem Context solves                           |
| **Performance**          | Split contexts to prevent unnecessary re-renders |
| **useReducer + Context** | Pattern for complex state management             |

<Card title="Next Steps" icon="arrow-right">
  In the next chapter, you'll learn about **React Router** — building multi-page applications with client-side routing!
</Card>

***

## Interview Deep-Dive

<AccordionGroup>
  <Accordion title="When a Context provider's value changes, every consumer re-renders. How do you prevent unnecessary re-renders in components that only use part of the context?">
    **Strong Answer:**
    This is the fundamental performance limitation of Context, and there is no built-in way to subscribe to a "slice" of context. When the Provider's value changes (by reference), every component calling `useContext` for that context re-renders -- even if it only reads `theme` and `cart` was the thing that changed.

    Three solutions, each with different tradeoffs:

    First, split contexts. Instead of one `AppContext` with theme and cart, create `ThemeContext` and `CartContext`. Components consuming only theme are unaffected by cart changes. This is the simplest and most recommended approach. The cost is more Provider components wrapping your app, but that has negligible runtime impact.

    Second, memoize the context value. Wrap the value object in `useMemo`: `const value = useMemo(() => ({ theme, toggleTheme }), [theme])`. This prevents re-renders when the *provider* component re-renders for unrelated reasons (like a parent state change), because the value reference stays stable. But it does NOT prevent re-renders when the actual context data changes -- that is by design.

    Third, separate state and dispatch contexts. Inspired by Redux, you create two contexts: one for the state (which changes), one for the dispatch function (which is stable). Components that only dispatch actions (like an "Add to Cart" button) consume the dispatch context and never re-render when state changes. Components that read state consume the state context. This dramatically reduces re-renders in action-heavy UIs.

    What most people miss: you can also use `React.memo` on child components of the consumer to prevent their subtree from re-rendering. If `CartBadge` uses context but wraps its children in memo, the children are protected even when the badge re-renders.

    **Follow-up: Some developers avoid Context for frequently changing values (like a real-time cursor position) and use Zustand or Jotai instead. Why?**

    Context re-renders ALL consumers on every value change, and there is no granular subscription mechanism. For a cursor position updating at 60fps, every component in the context tree re-renders 60 times per second. Even with React.memo on most children, the consumers themselves still re-render and run their function bodies.

    Zustand and Jotai use external stores with selector-based subscriptions. A component subscribes to a specific slice of state: `useStore(state => state.cursor)`. When cursor changes, only components that selected cursor re-render. Components that selected `state.theme` are completely untouched. This is possible because these libraries bypass React's context mechanism and use `useSyncExternalStore` (React 18) to trigger granular re-renders.

    The practical threshold: if context value changes more than a few times per second or has many consumers (50+), switch to an external store. For infrequently changing values like theme, locale, and auth status, Context is perfectly adequate and simpler.
  </Accordion>

  <Accordion title="Walk me through the 'Provider hell' problem and how you solve it in a large application with many contexts.">
    **Strong Answer:**
    Provider hell is when your root component has 10+ nested Providers, each wrapping the previous one. It looks like a pyramid of doom in JSX and makes the component tree hard to reason about in DevTools. The order of providers can matter (an AuthProvider that depends on an ApiProvider must be nested inside it), which creates implicit dependency chains.

    The simplest fix is a `ComposeProviders` utility component that takes an array of providers and nests them automatically:

    ```jsx theme={null}
    function ComposeProviders({ providers, children }) {
      return providers.reduceRight(
        (acc, Provider) => <Provider>{acc}</Provider>,
        children
      );
    }

    // Usage
    <ComposeProviders providers={[ThemeProvider, AuthProvider, CartProvider, NotificationProvider]}>
      <App />
    </ComposeProviders>
    ```

    This is cleaner but does not solve the underlying question: should you have this many contexts?

    For large applications, I evaluate each context: does it need to be global? Auth and theme are truly global. A shopping cart might be scoped to the e-commerce section. Form state should be local. Moving contexts to the lowest common ancestor reduces the blast radius of re-renders and makes the app structure clearer.

    Another approach: consolidate related contexts using useReducer. Instead of separate `UserContext`, `PreferencesContext`, and `NotificationsContext`, combine them into a single `AppContext` with a reducer that handles all three concerns. Split the state and dispatch into separate contexts for performance. This reduces Provider count while maintaining granular subscriptions.

    **Follow-up: How do you test components that depend on multiple contexts?**

    Create a test utility function that wraps components with the necessary providers pre-configured with test-friendly defaults:

    ```jsx theme={null}
    function renderWithProviders(ui, { user = mockUser, theme = 'light' } = {}) {
      return render(
        <AuthProvider initialUser={user}>
          <ThemeProvider initialTheme={theme}>
            {ui}
          </ThemeProvider>
        </AuthProvider>
      );
    }
    ```

    This pattern (which React Testing Library's docs recommend) lets each test specify only the overrides it cares about. For unit tests of a component that uses `useAuth`, you can mock the hook directly: `vi.mock('./useAuth', () => ({ useAuth: () => ({ user: testUser }) }))`. This avoids setting up the full provider tree when you only need one context value.

    For integration tests, use the real providers with controlled initial state. This tests that the context wiring actually works end-to-end, catching bugs like a missing Provider or wrong context reference.
  </Accordion>

  <Accordion title="Compare Context API with useReducer versus Redux Toolkit for managing global state. When does Context stop being sufficient?">
    **Strong Answer:**
    Context plus useReducer is essentially a lightweight, built-in Redux. You get a central store (context value), actions (dispatched objects), a reducer (pure function), and subscriptions (useContext consumers). For small to medium apps, this is often all you need.

    Context stops being sufficient at three thresholds. First, performance: when you have many consumers and frequent state updates, Context's all-or-nothing re-render behavior becomes a bottleneck. Redux's `useSelector` with shallow equality checking only re-renders components whose selected slice changed. For a dashboard with 50 widgets reading different parts of global state, Redux prevents 49 unnecessary re-renders that Context would trigger.

    Second, middleware and side effects: Redux Toolkit's `createAsyncThunk` and middleware pipeline give you a structured way to handle async logic, logging, error reporting, and optimistic updates. With Context, you manually implement all of this in your Provider or custom hooks. It is doable but messy at scale.

    Third, developer tools: Redux DevTools offer time-travel debugging, action replay, state diffs, and export/import of state snapshots. This is invaluable for debugging complex state transitions in production. Context has no equivalent tooling.

    My decision framework: start with local state. When multiple components need shared state, use Context. When you hit performance issues with Context or need more than 5-6 actions with complex async logic, evaluate Redux Toolkit or Zustand. For server state specifically (API data with caching, pagination, optimistic updates), skip Redux entirely and use React Query.

    **Follow-up: Zustand has gained popularity over Redux for many teams. What makes it different and when would you still choose Redux?**

    Zustand is dramatically simpler: no Providers, no wrapping your app, no actions/reducers pattern. You create a store with `create()`, define state and updaters as a plain object, and consume with a hook that supports selectors. The entire API is 5 functions. Bundle size is around 1KB.

    I would still choose Redux Toolkit when: the team is large and needs enforced patterns (actions, reducers, slices give structure that Zustand does not impose), when you need the DevTools time-travel debugging for complex state machines, when you have significant async logic that benefits from createAsyncThunk and middleware, or when the project already uses Redux and migration cost outweighs Zustand's simplicity.

    I would choose Zustand when: the team is small, state logic is straightforward, bundle size matters (Zustand is 10x smaller than Redux Toolkit), and you value API simplicity over structural patterns. Zustand is particularly good for state that is not connected to React at all (like a game engine or a shared state between React and vanilla JS).
  </Accordion>
</AccordionGroup>
