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

# Admin navigation

> Order, extend, customize, or replace FrogBot's admin sidebar.

FrogBot's default admin sidebar lists your collections, followed by a bottom rail. It supports desktop collapse, mobile navigation, active links, and saved section state without additional configuration. Add navigation items and sections, such as a New Chat link or Recents, when your app needs them.

## Configure sections

`admin.components.navSections` is an ordered array of component import paths. By default, FrogBot renders only `CollectionsSection`:

```ts theme={"theme":"tokyo-night"}
admin: {
  components: {
    navSections: ['@frogbotai/ui/admin/rsc#CollectionsSection']
  }
}
```

Setting `navSections` replaces that default array, so include every section you want rendered. Sections appear after configured `admin.components.navItems`, in array order. Plugin navigation items follow configured items. The bottom rail renders after all sections.

Add Recents by including `@frogbotai/ui/admin/rsc#RecentsSection` where you want it to appear:

```ts theme={"theme":"tokyo-night"}
admin: {
  components: {
    navSections: [
      '@frogbotai/ui/admin/rsc#CollectionsSection',
      '@frogbotai/ui/admin/rsc#RecentsSection'
    ]
  }
}
```

`NavSection` gives each section a title, collapsible body, and scrolling content. Its collapsed state is saved for the current user under `frogbot-nav-section:{id}` and restored on reload. Give every section a stable, unique `id`.

## Recents behavior

Recents appears when you add `RecentsSection` to `navSections`. FrogBot server-renders up to 30 chats available to the signed-in user, ordered by latest message, then refreshes the list after chat mutations, when the window regains focus, and every 30 seconds.

Chats are grouped under Today, Yesterday, Previous 7 days, Previous 30 days, and Older. Empty groups are hidden. The current chat remains active for its nested admin routes, and View all opens the configured chats collection list.

## Collection icons and placement

Set `admin.icon` on a collection to a built-in kebab-case icon name or a component import path. Your editor autocompletes every built-in name, including `robot`, `bubble-chat`, `file`, `link-square`, `ai-search`, and `people`.

```ts theme={"theme":"tokyo-night"}
import type { CollectionConfig } from 'frogbot'

export const Projects: CollectionConfig = {
  slug: 'projects',
  admin: {
    icon: 'robot',
    group: null,
  },
  fields: [],
}
```

Use `group: null` to list a collection as a top-level link above the sections. Leave `group` undefined to list it directly under the Collections heading, as FrogBot's built-in collections are, provide a string or localized value to list it under a named group label, or use `false` to hide only the navigation link. Both options are listed with the other collection admin settings in [Admin Options](https://docs.frogbot.ai/configuration/collections.md#admin-options).

Component icons receive `size` and `className` props and are added to the generated import map:

```ts theme={"theme":"tokyo-night"}
admin: {
  icon: './src/components/ProjectsIcon#ProjectsIcon'
}
```

A plain string without `#` must be a built-in icon name. An unknown name, such as a typo, stops startup with `[frogbot] Unknown admin icon '<name>'` followed by the valid names. A component path always names its export after `#`; write `'./src/components/ProjectsIcon#default'` for a default export. Navigation item and settings icons follow the same rules.

Within `CollectionsSection`, ungrouped collections come first, in configuration order. Named groups follow in first-seen order, with their collections in configuration order.

When the sidebar is collapsed, section headings and labels are hidden and each item displays its label in a tooltip. Section state and named-group state persist for the current user.

## Add a navigation item

`admin.components.navItems` holds the links above your collections. It is empty by default. Each item requires a `label` and a full `path`. Set `icon` to a built-in kebab-case icon name or a component import path, following the [icon rules](#collection-icons-and-placement).

```ts theme={"theme":"tokyo-night"}
admin: {
  components: {
    navItems: [
      {
        label: 'Operations',
        path: '/operations',
        icon: './src/components/OperationsIcon#OperationsIcon'
      }
    ]
  }
}
```

To give users a one-click way to start a chat, add a New Chat item that points at the chats collection's create page:

```ts theme={"theme":"tokyo-night"}
admin: {
  components: {
    navItems: [
      { label: 'New Chat', path: '/collections/chats/create', icon: 'pencil-edit' },
      { label: 'Operations', path: '/operations' }
    ]
  }
}
```

Paths include the admin route, which is `/` by default. Use your own admin route and chats collection slug in that path when either is customized.

Component icons receive `size` and `className` props and are added to the generated import map.

Configured items are application-authored links and are not filtered by collection permissions or visibility. Apply any required access behavior in the component or destination.

### Navigation item options

Each `navItems` entry is a `NavItem`:

| Option | Type | Default | Description |
| - | - | - | - |
| `label` | `string` | Required | Text shown in the sidebar and in the collapsed-sidebar tooltip. |
| `path` | `string` | Required | Link destination, used as is. Include the admin route, such as `/admin/operations` when the admin route is `/admin`. |
| `icon` | `AdminIcon` | Folder icon | Built-in kebab-case icon name or component import path, following the [icon rules](#collection-icons-and-placement). An unknown built-in name stops startup. |

## Copy and customize a section

The default sections and their prop types are exported from `@frogbotai/ui/admin/rsc`. Copy `RecentsSection` into your application when you need to change its query, heading, or server-rendered content. Keep its server component contract and import the reusable client renderer from the public client entry point:

```tsx theme={"theme":"tokyo-night"}
import { NavSection } from '@frogbotai/ui'
import type { RecentsSectionProps } from '@frogbotai/ui/admin/rsc'
import {
  RecentsSectionClient,
  type RecentsSectionClientProps
} from '@frogbotai/ui/admin'
import { getCachedFrogBot } from 'frogbot'
import { formatAdminURL } from 'payload/shared'

export async function WorkspaceRecents({ payload, req }: RecentsSectionProps) {
  const chat = getCachedFrogBot()?.config.chat
  const chatsSlug = chat?.enabled ? chat.chatsSlug : ''
  let recents: RecentsSectionClientProps['recents'] = []
  let collectionPath = ''

  if (chat?.enabled && req?.user) {
    const result = await payload.find({
      collection: chat.chatsSlug,
      depth: 0,
      limit: 30,
      overrideAccess: false,
      req,
      sort: '-lastMessageAt'
    })
    collectionPath = formatAdminURL({
      adminRoute: payload.config.routes.admin,
      path: `/collections/${chat.chatsSlug}`
    })
    recents = result.docs.map(doc => ({
      id: doc.id,
      title: typeof doc.title === 'string' && doc.title ? doc.title : 'Untitled Chat',
      agent: typeof doc.agent === 'string' ? doc.agent : '',
      lastMessageAt: typeof doc.lastMessageAt === 'string' ? doc.lastMessageAt : null
    }))
  }

  return (
    <NavSection id="workspace-recents" title="Recents">
      <RecentsSectionClient
        chatsSlug={chatsSlug}
        collectionPath={collectionPath}
        recents={recents}
      />
    </NavSection>
  )
}
```

`RecentsSectionClient` preserves live refresh, date buckets, active rows, and View all. Its public `RecentsSectionClientProps` contract requires `chatsSlug`, `collectionPath`, and normalized `recents`. `bucketRecents` is exported from `@frogbotai/ui` when only the default grouping is needed.

```ts theme={"theme":"tokyo-night"}
admin: {
  components: {
    navSections: [
      '@frogbotai/ui/admin/rsc#CollectionsSection',
      './src/components/WorkspaceRecents#WorkspaceRecents'
    ]
  }
}
```

Component paths in `navSections` and admin component slots use `FrogBotComponent`. This type accepts either an import-map path string or a component definition. Collection, navigation item, and settings icons use `AdminIcon`, which also accepts built-in names; its `IconName` member lists them. Both types are exported from `frogbot` for reusable configuration types.

Chat-wide component slots are grouped under `admin.components.chat`. Use `Chat` to replace the canonical conversation surface, or `UserMessageActions` and `AssistantMessageActions` to customize only role-specific controls. See [Chat persistence](https://docs.frogbot.ai/chat/overview.md#customize-admin-chat-components).

## Register a settings page

Add pages to the Settings card grid with the root `settings` array. Each entry requires a `label`, a relative `path`, and a `Component`. Paths may be nested, such as `security/audit`, but every normalized path must be unique. Do not include `/settings` or a leading slash.

```ts theme={"theme":"tokyo-night"}
import type { SettingsEntry } from 'frogbot'

const usageSettings: SettingsEntry = {
  label: 'Usage',
  path: 'usage',
  Component: './src/settings/Usage#Usage',
  icon: './src/settings/UsageIcon#UsageIcon',
  access: ({ req }) => Boolean(req.user)
}

export default buildConfig({
  settings: [usageSettings]
})
```

`icon` and `access` are optional. Set `icon` to a built-in icon name, such as `'robot'`, or a component import path, following the [icon rules](#collection-icons-and-placement). Without `access`, FrogBot allows any authenticated user. The access function runs before FrogBot shows the card or renders the page. The page component and component icons are added to the generated import map.

Each Settings page names itself in the browser tab, followed by `admin.meta.titleSuffix`: `Collections - FrogBot` on the collection cards and the entry's label on its page, such as `Usage - FrogBot`. An unknown path, or an entry the user cannot open, reads `Settings - FrogBot`. The server title already names the page, so the tab doesn't change after it loads.

### Settings entry options

Each `settings` entry is a `SettingsEntry`:

| Option | Type | Default | Description |
| - | - | - | - |
| `label` | `string` | Required | Card and sidebar label in the Settings area, the page heading, and the browser tab title. |
| `path` | `string` | Required | Route relative to `/settings`, such as `usage` or `security/audit`. It must not be empty, start with `/`, contain `\`, `?`, `#`, empty segments, `.`, or `..`, or repeat another entry's path; any of these stops startup. |
| `Component` | `FrogBotComponent` | Required | Component rendered for the page. Requests to nested paths below `path` also render it. |
| `icon` | `AdminIcon` | Settings icon | Built-in icon name or component import path, following the [icon rules](#collection-icons-and-placement). |
| `access` | `Access` | Signed-in users | Called with `{ req }` only; other access arguments are undefined. A truthy result shows the card and allows the page. Otherwise the page shows Page not found under a `Settings` heading, and its tab title and metadata read `Settings`, so the label stays hidden. |

Plugins append entries after existing settings so they compose in plugin order:

```ts theme={"theme":"tokyo-night"}
return {
  ...config,
  settings: [
    ...(config.settings ?? []),
    {
      label: 'Usage',
      path: 'usage',
      Component: '@workspace/plugin/client#UsageSettings'
    }
  ]
}
```

The usage reports plugin follows this convention; its Usage entry appears only when the plugin is installed.

Registry entries add pages within FrogBot's default Settings shell; they do not override its core routes. To replace the entire Settings surface, configure a custom root view:

```ts theme={"theme":"tokyo-night"}
admin: {
  components: {
    views: {
      settings: {
        Component: './src/components/Settings#Settings',
        exact: false,
        path: '/settings'
      }
    }
  }
}
```

## Customize the bottom rail

Account and Settings appear in the default bottom rail. Add components around them with `admin.components.beforeBottomRail` and `admin.components.afterBottomRail`. Use `beforeSidebarClose` for controls immediately left of the expanded sidebar close button.

```ts theme={"theme":"tokyo-night"}
admin: {
  components: {
    beforeBottomRail: ['./src/components/WorkspaceSwitcher#WorkspaceSwitcher'],
    afterBottomRail: ['./src/components/SupportLink#SupportLink'],
    beforeSidebarClose: ['./src/components/SidebarAction#SidebarAction']
  }
}
```

## Customize the account menu

Select Account in the bottom rail to open the account menu. Its items appear in this order:

1. Your name and email, which open the account page
2. `admin.components.beforeAccountMenu`
3. Settings
4. `admin.components.afterAccountMenu`
5. `admin.components.settingsMenu`
6. Log out, or `admin.components.logout.Button` when set

```ts theme={"theme":"tokyo-night"}
admin: {
  components: {
    beforeAccountMenu: ['./src/components/WorkspaceSwitcher#WorkspaceSwitcher'],
    afterAccountMenu: ['./src/components/SupportLink#SupportLink'],
    logout: { Button: './src/components/LogoutButton#LogoutButton' }
  }
}
```

Replace Log out when users sign out somewhere else, such as an external identity app:

```tsx theme={"theme":"tokyo-night"}
import type { RootComponentServerProps } from 'frogbot'

export function LogoutButton({ user }: RootComponentServerProps) {
  const returnTo = encodeURIComponent('https://app.example.com')

  return (
    <a href={`https://identity.example.com/sign-out?returnTo=${returnTo}`} role="menuitem">
      Sign out {user?.email}
    </a>
  )
}
```

Account menu components receive the request as `req`. Use `req?.frogbot` to reach FrogBot. See [Component props](https://docs.frogbot.ai/custom-components/root-components.md#component-props) for the full list.

## Replace the sidebar

Set `admin.components.Nav` to replace the default sidebar. Your component receives the standard admin server props, including the current user, permissions, and visible collections.

```ts theme={"theme":"tokyo-night"}
admin: {
  components: {
    Nav: './src/components/WorkspaceNav#WorkspaceNav'
  }
}
```

A custom `Nav` fully owns navigation rendering. It does not receive FrogBot's default sidebar styles, configured items or sections, bottom rail, or shell slots automatically.

## Use the sidebar outside the admin

The sidebar doesn't need the admin panel. `NavShell`, `AppSidebar`, `NavSection`, `NavItem`, `AccountMenu` and `RecentChats` are exported from `@frogbotai/ui`, so a browser extension's side panel or your own app can show the same sidebar with `@frogbotai/ui/styles.css` alone.

`NavAdapterProvider` tells the sidebar how your app links and navigates. Every prop is optional: without it, links are plain anchors, the current path comes from `location`, navigating sets `location`, and preferences, such as a collapsed section, live in memory. Define `Link` outside your component, as any React component: a new `Link` on each render remounts every link and moves keyboard focus off the one just pressed. Render the sidebar inside a [`ThemeProvider`](https://docs.frogbot.ai/chat/ui-theming#themeprovider), like any FrogBot component outside the admin, so it gets FrogBot's font and light or dark mode.

```tsx theme={"theme":"tokyo-night"}
import {
  NavAdapterProvider,
  type NavLinkProps,
  NavSection,
  NavShell,
  RecentChats,
  useNavAdapter,
} from '@frogbotai/ui'
import { ThemeProvider } from '@frogbotai/ui/theme'

const preferences = {
  get: async (key: string) => (await chrome.storage.local.get(key))[key],
  set: (key: string, value: unknown) => chrome.storage.local.set({ [key]: value }),
}

export function PanelNav({ adapter, recents }: PanelNavProps) {
  const [view, setView] = useState('/chat')

  return (
    <ThemeProvider>
      <NavAdapterProvider pathname={view} navigate={setView} Link={PanelLink} preferences={preferences}>
        <NavShell
          homePath="/chat"
          navItems={[{ label: 'Providers', path: '/providers', icon: 'key-round' }]}
          sections={
            <NavSection id="recents" title="Recents">
              <RecentChats
                adapter={adapter}
                recents={recents}
                chatPath={(id) => `/chat/${id}`}
                viewAllPath="/chats"
              />
            </NavSection>
          }
        />
      </NavAdapterProvider>
    </ThemeProvider>
  )
}

// Opens a view in the panel instead of loading a page.
function PanelLink({ href, ...props }: NavLinkProps) {
  const { navigate } = useNavAdapter()

  return (
    <a
      {...props}
      href={href}
      onClick={(event) => {
        event.preventDefault()
        navigate(href)
      }}
    />
  )
}
```

| Prop | Type | Default | Description |
| - | - | - | - |
| `Link` | `ComponentType<NavLinkProps>` | An `<a>` | Renders every link, with `href`, `className`, `aria-current` and the children. |
| `pathname` | `string` | `location.pathname` | The current path, which marks the active item. |
| `navigate` | `(path: string) => void` | `location.assign` | Called by sidebar buttons, such as a nav item on the rail or Home. |
| `preferences` | `{ get(key), set(key, value) }` | In memory | Where the sidebar saves its open state (under `nav`) and each section's collapsed state. |
| `drawer` | `{ open, setOpen, hydrated, ref? }` | The shell's own state | The open state, when your layout also needs it. |

Below 768px the shell is a drawer with a menu button, a backdrop and Escape to close it; on wider screens it is a rail that collapses to icons, with a label tooltip on hover. Without `accountPath` the bottom rail has no account menu.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.