# How BotContext Manages Bot State in the y-gui React Frontend

> Discover how y-gui's React frontend manages bot state using context. Learn about fetching bots, tracking selection, and updating state across components.

- Repository: [luohy15/y-gui](https://github.com/luohy15/y-gui)
- Tags: how-to-guide
- Published: 2026-03-06

---

**The y-gui frontend centralizes bot state management through a React context provider that fetches available bots from the backend, tracks the currently selected bot, and exposes setter functions to components throughout the application.**

The y-gui repository implements a dedicated React context to manage bot state across its frontend application. This **BotContext** pattern ensures that bot configurations, selection state, and update mechanisms remain accessible to any component in the component tree without prop drilling.

## BotContext Architecture and Data Flow

The state management architecture centers on the `BotProvider` component defined in [`frontend/src/contexts/BotContext.tsx`](https://github.com/luohy15/y-gui/blob/main/frontend/src/contexts/BotContext.tsx). This provider encapsulates both the data fetching logic and the local state for tracking user selection.

### Fetching Bot Configurations from the Backend

The provider initializes bot data by calling the `/api/bots` endpoint through the `useAuthenticatedSWR` hook. This returns an array of `BotConfig` objects representing all available bots configured in the system.

```typescript
// From frontend/src/contexts/BotContext.tsx
const { data, isLoading } = useAuthenticatedSWR<BotConfig[]>('/api/bots');

```

The hook handles authentication headers and caching automatically, ensuring components receive fresh data while minimizing redundant network requests.

### Tracking the Currently Selected Bot

Within the provider, a `selectedBot` state variable stores the currently active bot identifier as a `string | undefined`. The implementation automatically selects the first available bot when the data loads initially:

```typescript
// From frontend/src/contexts/BotContext.tsx
useEffect(() => {
  if (data && data.length > 0 && !selectedBot) {
    setSelectedBot(data[0].name);
  }
}, [data, selectedBot]);

```

The `setSelectedBot` dispatcher is exposed through the context value, allowing descendant components to update the active bot programmatically.

## Consuming and Updating Bot State in Components

Components throughout the y-gui application access bot state through the `useBot` custom hook, which provides type-safe access to the context value.

### The useBot Hook for Safe Context Access

The `useBot` hook wraps React's `useContext` and enforces that components can only access bot state when rendered within a `BotProvider`:

```typescript
// From frontend/src/contexts/BotContext.tsx
export function useBot() {
  const context = useContext(BotContext);
  if (context === undefined) {
    throw new Error('useBot must be used within a BotProvider');
  }
  return context;
}

```

This pattern prevents runtime errors and ensures developers receive immediate feedback when attempting to use bot state outside the provider hierarchy.

### Persisting Bot Selection Per Chat

The `MessageInput` component implements per-chat persistence by synchronizing the selected bot to `localStorage`. This ensures that when users navigate between different chat sessions, each maintains its own bot preference.

```typescript
// From frontend/src/components/MessageInput.tsx
useEffect(() => {
  const savedBot = localStorage.getItem(`chat_${chatId}_selectedBot`);
  if (savedBot) {
    setSelectedBot(savedBot);
  } else if (bots?.length) {
    setSelectedBot(bots[0].name);
  }
}, [bots, chatId]);

useEffect(() => {
  if (selectedBot) {
    localStorage.setItem(`chat_${chatId}_selectedBot`, selectedBot);
  }
}, [selectedBot, chatId]);

```

The storage key follows the pattern `chat_<id>_selectedBot`, isolating preferences between different conversation threads.

### CRUD Operations and State Refresh

The `BotSection` component in the Settings page manages bot lifecycle operations. After creating, updating, or deleting bots through the backend API, it triggers a context refresh to update the global state.

```typescript
// From frontend/src/components/Settings/BotSection.tsx
const { data: bots, mutate: mutateBots } = useAuthenticatedSWR<BotConfig[]>('/api/bots');

// After mutation
await api.post('/api/bot', newBot);
mutateBots();  // Refetches /api/bots, updating BotContext automatically

```

The `mutateBots` function invalidates the SWR cache and re-fetches the bot list, ensuring that `BotContext` receives the updated data and propagates changes to all consuming components.

## Summary

- **BotContext** centralizes bot state in [`frontend/src/contexts/BotContext.tsx`](https://github.com/luohy15/y-gui/blob/main/frontend/src/contexts/BotContext.tsx), providing a single source of truth for available bots and the current selection.
- The `BotProvider` fetches bot configurations from `/api/bots` using `useAuthenticatedSWR` and automatically selects the first bot on initial load.
- Components access state through the `useBot` hook, which enforces provider boundaries and prevents undefined context errors.
- Per-chat persistence is implemented in `MessageInput` using `localStorage` keys formatted as `chat_<id>_selectedBot`.
- CRUD operations in `BotSection` trigger SWR mutations to refresh the global bot list and maintain synchronization across the application.

## Frequently Asked Questions

### How does BotContext handle authentication when fetching bots?

The context uses the `useAuthenticatedSWR` hook, which automatically includes authentication headers in the request to `/api/bots`. This ensures that only authorized users can retrieve the bot configuration list, and the hook handles token management and request deduplication.

### What happens if no bots are configured in the backend?

If the `/api/bots` endpoint returns an empty array, the `selectedBot` state remains `undefined`. Components consuming the context should handle this case by disabling bot-dependent features or displaying empty states. The `isLoading` flag from SWR helps distinguish between loading and empty states.

### Can multiple components modify the selected bot simultaneously?

Yes, any component that calls `useBot()` receives the `setSelectedBot` dispatcher. When invoked, it updates the context value, triggering a re-render of all components that consume the context. This pattern ensures UI consistency across the application without prop drilling.

### How is the bot selection persisted across page refreshes?

While `BotContext` itself does not persist state to storage, consuming components like `MessageInput` implement persistence by writing the selected bot to `localStorage` using chat-specific keys (`chat_<id>_selectedBot`). On component mount, these components read from `localStorage` and call `setSelectedBot` to restore the previous selection.