# Sidebar

> A collapsible sidebar with multiple visual style options.

## Usage

The Sidebar component is a standalone, fixed sidebar that pushes the page content. On desktop, it renders inline and can be collapsed; on mobile, it opens a [Modal](https://bitrix24.github.io/b24ui/raw/docs/components/modal.md), [Slideover](https://bitrix24.github.io/b24ui/raw/docs/components/slideover.md) or [Drawer](https://bitrix24.github.io/b24ui/raw/docs/components/drawer.md) component.

> [!TIP]
> See: /docs/components/dashboard-sidebar/
> Sidebar vs DashboardSidebar: This component is a simple, standalone sidebar you can drop anywhere (chat panel, settings, navigation). If you need drag-to-resize, state persistence and integration with [DashboardGroup](/docs/components/dashboard-group/), use [DashboardSidebar](/docs/components/dashboard-sidebar/) instead.

Use the `header`, `default` and `footer` slots to customize the sidebar content. The `v-model:open` directive is viewport-aware: on desktop it controls the expanded/collapsed state, on mobile it controls the menu.

```vue [SidebarExample.vue]
<script setup lang="ts">
import type { DropdownMenuItem, NavigationMenuItem } from '@bitrix24/b24ui-nuxt'
import HomeIcon from '@bitrix24/b24icons-vue/outline/HomeIcon'
import MailIcon from '@bitrix24/b24icons-vue/outline/MailIcon'
import PaymentIcon from '@bitrix24/b24icons-vue/outline/PaymentIcon'
import CirclePlusIcon from '@bitrix24/b24icons-vue/outline/CirclePlusIcon'
import PulseCircleIcon from '@bitrix24/b24icons-vue/main/PulseCircleIcon'
import CollabIcon from '@bitrix24/b24icons-vue/outline/CollabIcon'
import SettingsIcon from '@bitrix24/b24icons-vue/main/SettingsIcon'
import ThreePersonsIcon from '@bitrix24/b24icons-vue/outline/ThreePersonsIcon'
import UserIcon from '@bitrix24/b24icons-vue/common-b24/UserIcon'
import ContrastIcon from '@bitrix24/b24icons-vue/outline/ContrastIcon'
import SunIcon from '@bitrix24/b24icons-vue/outline/SunIcon'
import MoonIcon from '@bitrix24/b24icons-vue/outline/MoonIcon'
import GitHubIcon from '@bitrix24/b24icons-vue/social/GitHubIcon'
import LogOutIcon from '@bitrix24/b24icons-vue/outline/LogOutIcon'
import CloseChatIcon from '@bitrix24/b24icons-vue/outline/CloseChatIcon'

const open = ref(true)

const colorMode = useColorMode()

const teams = ref([
  {
    label: 'Assistant Name',
    avatar: {
      src: '/b24ui/avatar/assistant.png',
      alt: 'assistant',
      loading: 'lazy' as const
    }
  },
  {
    label: 'Bitrix24',
    avatar: {
      src: 'https://github.com/bitrix24.png',
      alt: 'bitrix24',
      loading: 'lazy' as const
    }
  },
  {
    label: 'Employee Name',
    avatar: {
      src: '/b24ui/avatar/employee.png',
      alt: 'employee',
      loading: 'lazy' as const
    }
  }
])
const selectedTeam = ref(teams.value[0])
const selectedTeamFormatted = computed(() => {
  return open.value
    ? selectedTeam.value
    : {
        avatar: selectedTeam.value!.avatar
      }
})

const teamsItems = computed<DropdownMenuItem[][]>(() => {
  return [
    teams.value.map((team, index) => ({
      ...team,
      kbds: ['meta', String(index + 1)],
      onSelect() {
        selectedTeam.value = team
      }
    })),
    [
      { label: 'Create team', icon: CirclePlusIcon }
    ]
  ]
})

function getItems(state: 'collapsed' | 'expanded') {
  return [
    {
      label: 'Inbox',
      icon: MailIcon,
      badge: '4'
    },
    {
      label: 'Issues',
      icon: CollabIcon
    },
    {
      label: 'Actions',
      icon: PulseCircleIcon
    },
    {
      label: 'Settings',
      icon: SettingsIcon,
      defaultOpen: true,
      children: state === 'expanded'
        ? [
            {
              label: 'General',
              icon: HomeIcon
            },
            {
              label: 'Team',
              icon: ThreePersonsIcon
            },
            {
              label: 'Billing',
              icon: PaymentIcon
            }
          ]
        : []
    }
  ] satisfies NavigationMenuItem[]
}

const user = ref({
  name: 'Bitrix24',
  avatar: {
    src: 'https://github.com/bitrix24.png',
    alt: 'bitrix24'
  }
})

const userItems = computed<DropdownMenuItem[][]>(() => (
  [
    [
      {
        label: 'Profile',
        icon: UserIcon
      },
      {
        label: 'Billing',
        icon: PaymentIcon
      },
      {
        label: 'Settings',
        icon: SettingsIcon,
        to: '/templates/'
      }
    ],
    [
      {
        label: 'Appearance',
        icon: ContrastIcon,
        children: [
          {
            label: 'Light',
            icon: SunIcon,
            type: 'checkbox',
            checked: colorMode.value === 'light',
            onUpdateChecked(checked: boolean) {
              if (checked) {
                colorMode.preference = 'light'
              }
            },
            onSelect(e: Event) {
              e.preventDefault()
            }
          },
          {
            label: 'Dark',
            icon: MoonIcon,
            type: 'checkbox',
            checked: colorMode.value === 'dark',
            onUpdateChecked(checked: boolean) {
              if (checked) {
                colorMode.preference = 'dark'
              }
            },
            onSelect(e: Event) {
              e.preventDefault()
            }
          }
        ]
      }
    ],
    [
      {
        label: 'GitHub',
        icon: GitHubIcon,
        to: 'https://github.com/bitrix24/b24ui',
        target: '_blank'
      },
      {
        label: 'Log out',
        icon: LogOutIcon
      }
    ]
  ]
))

defineShortcuts(extractShortcuts(teamsItems.value))
</script>

<template>
  <div class="flex flex-1">
    <B24Sidebar
      v-model:open="open"
      collapsible="icon"
      rail
      :b24ui="{
        container: 'h-full',
        inner: 'bg-elevated/25 divide-transparent',
        body: 'py-0'
      }"
    >
      <template #header>
        <B24DropdownMenu
          :items="teamsItems"
          :content="{ align: 'start', collisionPadding: 12 }"
          :b24ui="{ content: 'w-[270px]', viewport: 'w-[270px] max-h-[62vh]' }"
        >
          <B24Button
            v-bind="selectedTeamFormatted"
            :use-dropdown="open"
            color="air-tertiary"
            class="w-full data-[state=open]:bg-(--ui-btn-background-hover) overflow-hidden"
            :b24ui="{ trailingIcon: 'text-dimmed ms-auto', label: 'flex-1' }"
          />
        </B24DropdownMenu>
      </template>

      <template #default="{ state }">
        <B24NavigationMenu
          :key="state"
          :items="getItems(state)"
          orientation="vertical"
          :b24ui="{ link: 'p-1.5 overflow-hidden' }"
        />
      </template>

      <template #footer>
        <B24DropdownMenu
          :items="userItems"
          :content="{ align: 'start', side: 'top', sideOffset: 4, collisionPadding: 12 }"
          :b24ui="{ content: 'w-[200px]', viewport: 'w-[200px] max-h-[62vh]' }"
        >
          <B24Button
            v-bind="user"
            :label="open ? user?.name : undefined"
            :use-dropdown="open"
            color="air-tertiary"
            class="w-full data-[state=open]:bg-(--ui-btn-background-hover) overflow-hidden"
            :b24ui="{ trailingIcon: 'text-dimmed ms-auto', label: 'flex-1' }"
          />
        </B24DropdownMenu>
      </template>
    </B24Sidebar>

    <div class="flex-1 flex flex-col">
      <div class="h-(--b24ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
        <B24Button
          :icon="CloseChatIcon"
          aria-label="Toggle sidebar"
          size="md"
          color="air-tertiary"
          @click="open = !open"
        />
      </div>

      <div class="flex-1 p-4">
        <Placeholder class="size-full" />
      </div>
    </div>
  </div>
</template>
```

### Variant

Use the `variant` prop to change the visual style of the sidebar. Defaults to `sidebar`.

```vue [SidebarPropsExample.vue]
<script setup lang="ts">
import type { NavigationMenuItem, SidebarProps } from '@bitrix24/b24ui-nuxt'
import HomeIcon from '@bitrix24/b24icons-vue/outline/HomeIcon'
import MailIcon from '@bitrix24/b24icons-vue/outline/MailIcon'
import ContactIcon from '@bitrix24/b24icons-vue/outline/ContactIcon'
import CloseChatIcon from '@bitrix24/b24icons-vue/outline/CloseChatIcon'
import OpenChatIcon from '@bitrix24/b24icons-vue/outline/OpenChatIcon'
import Bitrix24Icon from '@bitrix24/b24icons-vue/common-b24/Bitrix24Icon'
import B24Icon from '@bitrix24/b24icons-vue/main/B24Icon'

// Ignore the props for the example
defineProps<Pick<SidebarProps, 'variant' | 'collapsible' | 'side'>>()

const open = ref(true)

const items: NavigationMenuItem[] = [
  {
    label: 'Home',
    icon: HomeIcon,
    active: true
  },
  {
    label: 'Inbox',
    icon: MailIcon,
    badge: '4'
  },
  {
    label: 'Contacts',
    icon: ContactIcon
  }
]
</script>

<template>
  <div
    class="flex flex-1"
    :class="[
      variant === 'inset' && 'bg-(--ui-color-g-plastic-greish-bg) dark:bg-(--ui-color-base-black-fixed)',
      side === 'right' && 'flex-row-reverse'
    ]"
  >
    <B24Sidebar
      v-model:open="open"
      :variant="variant"
      :collapsible="collapsible"
      :side="side"
      :b24ui="{
        container: 'h-full'
      }"
    >
      <template #header>
        <Bitrix24Icon v-if="open" class="w-auto h-5 " />
        <B24Icon v-else class="w-auto h-8 text-[#2fc6f6]" />
      </template>

      <B24NavigationMenu
        :items="items"
        orientation="vertical"
        :b24ui="{ link: 'p-1.5 overflow-hidden' }"
      />
    </B24Sidebar>

    <div class="flex-1 flex flex-col overflow-hidden lg:peer-data-[variant=floating]:my-4 peer-data-[variant=inset]:m-4 lg:peer-data-[variant=inset]:not-peer-data-[collapsible=offcanvas]:ms-0 peer-data-[variant=inset]:rounded-xl peer-data-[variant=inset]:shadow-sm peer-data-[variant=inset]:ring peer-data-[variant=inset]:ring-default bg-default">
      <div
        class="h-(--b24ui-header-height) shrink-0 flex items-center px-4"
        :class="[
          variant !== 'floating' && 'border-b border-default',
          side === 'right' && 'justify-end'
        ]"
      >
        <B24Button
          :icon="side === 'right' ? OpenChatIcon : CloseChatIcon"
          color="air-tertiary"
          aria-label="Toggle sidebar"
          @click="open = !open"
        />
      </div>

      <div class="flex-1 p-4">
        <Placeholder class="size-full" />
      </div>
    </div>
  </div>
</template>
```

### Collapsible

Use the `collapsible` prop to change the collapse behavior of the sidebar. Defaults to `offcanvas`.

- `offcanvas`: The sidebar slides out of view completely.
- `icon`: The sidebar shrinks to icon-only width.
- `none`: The sidebar is not collapsible.

```vue [SidebarPropsExample.vue]
<script setup lang="ts">
import type { NavigationMenuItem, SidebarProps } from '@bitrix24/b24ui-nuxt'
import HomeIcon from '@bitrix24/b24icons-vue/outline/HomeIcon'
import MailIcon from '@bitrix24/b24icons-vue/outline/MailIcon'
import ContactIcon from '@bitrix24/b24icons-vue/outline/ContactIcon'
import CloseChatIcon from '@bitrix24/b24icons-vue/outline/CloseChatIcon'
import OpenChatIcon from '@bitrix24/b24icons-vue/outline/OpenChatIcon'
import Bitrix24Icon from '@bitrix24/b24icons-vue/common-b24/Bitrix24Icon'
import B24Icon from '@bitrix24/b24icons-vue/main/B24Icon'

// Ignore the props for the example
defineProps<Pick<SidebarProps, 'variant' | 'collapsible' | 'side'>>()

const open = ref(true)

const items: NavigationMenuItem[] = [
  {
    label: 'Home',
    icon: HomeIcon,
    active: true
  },
  {
    label: 'Inbox',
    icon: MailIcon,
    badge: '4'
  },
  {
    label: 'Contacts',
    icon: ContactIcon
  }
]
</script>

<template>
  <div
    class="flex flex-1"
    :class="[
      variant === 'inset' && 'bg-(--ui-color-g-plastic-greish-bg) dark:bg-(--ui-color-base-black-fixed)',
      side === 'right' && 'flex-row-reverse'
    ]"
  >
    <B24Sidebar
      v-model:open="open"
      :variant="variant"
      :collapsible="collapsible"
      :side="side"
      :b24ui="{
        container: 'h-full'
      }"
    >
      <template #header>
        <Bitrix24Icon v-if="open" class="w-auto h-5 " />
        <B24Icon v-else class="w-auto h-8 text-[#2fc6f6]" />
      </template>

      <B24NavigationMenu
        :items="items"
        orientation="vertical"
        :b24ui="{ link: 'p-1.5 overflow-hidden' }"
      />
    </B24Sidebar>

    <div class="flex-1 flex flex-col overflow-hidden lg:peer-data-[variant=floating]:my-4 peer-data-[variant=inset]:m-4 lg:peer-data-[variant=inset]:not-peer-data-[collapsible=offcanvas]:ms-0 peer-data-[variant=inset]:rounded-xl peer-data-[variant=inset]:shadow-sm peer-data-[variant=inset]:ring peer-data-[variant=inset]:ring-default bg-default">
      <div
        class="h-(--b24ui-header-height) shrink-0 flex items-center px-4"
        :class="[
          variant !== 'floating' && 'border-b border-default',
          side === 'right' && 'justify-end'
        ]"
      >
        <B24Button
          :icon="side === 'right' ? OpenChatIcon : CloseChatIcon"
          color="air-tertiary"
          aria-label="Toggle sidebar"
          @click="open = !open"
        />
      </div>

      <div class="flex-1 p-4">
        <Placeholder class="size-full" />
      </div>
    </div>
  </div>
</template>
```

> [!TIP]
> See: #slots
> You can access the `state` in the slot props to customize the content of the sidebar when it is collapsed.

### Side

Use the `side` prop to change the side of the sidebar. Defaults to `left`.

```vue [SidebarPropsExample.vue]
<script setup lang="ts">
import type { NavigationMenuItem, SidebarProps } from '@bitrix24/b24ui-nuxt'
import HomeIcon from '@bitrix24/b24icons-vue/outline/HomeIcon'
import MailIcon from '@bitrix24/b24icons-vue/outline/MailIcon'
import ContactIcon from '@bitrix24/b24icons-vue/outline/ContactIcon'
import CloseChatIcon from '@bitrix24/b24icons-vue/outline/CloseChatIcon'
import OpenChatIcon from '@bitrix24/b24icons-vue/outline/OpenChatIcon'
import Bitrix24Icon from '@bitrix24/b24icons-vue/common-b24/Bitrix24Icon'
import B24Icon from '@bitrix24/b24icons-vue/main/B24Icon'

// Ignore the props for the example
defineProps<Pick<SidebarProps, 'variant' | 'collapsible' | 'side'>>()

const open = ref(true)

const items: NavigationMenuItem[] = [
  {
    label: 'Home',
    icon: HomeIcon,
    active: true
  },
  {
    label: 'Inbox',
    icon: MailIcon,
    badge: '4'
  },
  {
    label: 'Contacts',
    icon: ContactIcon
  }
]
</script>

<template>
  <div
    class="flex flex-1"
    :class="[
      variant === 'inset' && 'bg-(--ui-color-g-plastic-greish-bg) dark:bg-(--ui-color-base-black-fixed)',
      side === 'right' && 'flex-row-reverse'
    ]"
  >
    <B24Sidebar
      v-model:open="open"
      :variant="variant"
      :collapsible="collapsible"
      :side="side"
      :b24ui="{
        container: 'h-full'
      }"
    >
      <template #header>
        <Bitrix24Icon v-if="open" class="w-auto h-5 " />
        <B24Icon v-else class="w-auto h-8 text-[#2fc6f6]" />
      </template>

      <B24NavigationMenu
        :items="items"
        orientation="vertical"
        :b24ui="{ link: 'p-1.5 overflow-hidden' }"
      />
    </B24Sidebar>

    <div class="flex-1 flex flex-col overflow-hidden lg:peer-data-[variant=floating]:my-4 peer-data-[variant=inset]:m-4 lg:peer-data-[variant=inset]:not-peer-data-[collapsible=offcanvas]:ms-0 peer-data-[variant=inset]:rounded-xl peer-data-[variant=inset]:shadow-sm peer-data-[variant=inset]:ring peer-data-[variant=inset]:ring-default bg-default">
      <div
        class="h-(--b24ui-header-height) shrink-0 flex items-center px-4"
        :class="[
          variant !== 'floating' && 'border-b border-default',
          side === 'right' && 'justify-end'
        ]"
      >
        <B24Button
          :icon="side === 'right' ? OpenChatIcon : CloseChatIcon"
          color="air-tertiary"
          aria-label="Toggle sidebar"
          @click="open = !open"
        />
      </div>

      <div class="flex-1 p-4">
        <Placeholder class="size-full" />
      </div>
    </div>
  </div>
</template>
```

### Title

Use the `title` prop to set the title of the sidebar header.

```vue
<template>
  <B24Sidebar title="Navigation">
<Placeholder class="h-full" />
</B24Sidebar>
</template>
```

### Description

Use the `description` prop to set the description of the sidebar header.

```vue
<template>
  <B24Sidebar title="Navigation" description="Browse your workspace">
<Placeholder class="h-full" />
</B24Sidebar>
</template>
```

### Rail

Use the `rail` prop to display a thin interactive edge on the sidebar that toggles the collapsed state on click. The rail is only rendered when `collapsible` is not `none`.

```vue
<template>
  <B24Sidebar rail collapsible="icon" title="Navigation">
<Placeholder class="h-full" />
</B24Sidebar>
</template>
```

### Close

Use the `close` prop to display a close button in the sidebar header. The close button is only rendered when `collapsible` is not `none`.

You can pass any property from the [Button](https://bitrix24.github.io/b24ui/raw/docs/components/button.md) component to customize it.

```vue
<template>
  <B24Sidebar close rail collapsible="icon" title="Navigation">
<Placeholder class="h-full" />
</B24Sidebar>
</template>
```

### Close Icon

Use the `close-icon` prop to customize the close button [Icon](https://bitrix24.github.io/b24icons/icons/).

```vue
<script setup lang="ts">
import RocketIcon from '@bitrix24/b24icons-vue/main/RocketIcon'
</script>

<template>
  <B24Sidebar close :close-icon="RocketIcon" rail collapsible="icon" side="right" title="Navigation">
<Placeholder class="h-full" />
</B24Sidebar>
</template>
```

### Mode

Use the `mode` prop to change the mode of the sidebar menu on mobile. Defaults to `slideover`.

```vue [SidebarModeExample.vue]
<script setup lang="ts">
import type { NavigationMenuItem, SidebarProps } from '@bitrix24/b24ui-nuxt'
import HomeIcon from '@bitrix24/b24icons-vue/outline/HomeIcon'
import MailIcon from '@bitrix24/b24icons-vue/outline/MailIcon'
import ContactIcon from '@bitrix24/b24icons-vue/outline/ContactIcon'
import OpenChatIcon from '@bitrix24/b24icons-vue/outline/OpenChatIcon'

// Ignore the props for the example
defineProps<Pick<SidebarProps, 'mode'>>()

const open = ref(true)

const items: NavigationMenuItem[] = [
  {
    label: 'Home',
    icon: HomeIcon,
    active: true
  },
  {
    label: 'Inbox',
    icon: MailIcon,
    badge: '4'
  },
  {
    label: 'Contacts',
    icon: ContactIcon
  }
]
</script>

<template>
  <div class="flex flex-1">
    <B24Sidebar v-model:open="open" :mode="mode" title="Navigation">
      <B24NavigationMenu
        :items="items"
        orientation="vertical"
        :b24ui="{ link: 'p-1.5 overflow-hidden' }"
      />
    </B24Sidebar>

    <div class="flex-1 flex flex-col">
      <div class="h-(--b24ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
        <B24Button
          :icon="OpenChatIcon"
          color="air-tertiary"
          aria-label="Toggle sidebar"
          @click="open = !open"
        />
      </div>

      <div class="flex-1 p-4">
        <Placeholder class="size-full" />
      </div>
    </div>
  </div>
</template>
```

> [!TIP]
> See: #props
> You can use the `menu` prop to customize the menu of the sidebar, it will adapt depending on the mode you choose.

## Examples

### Control open state

You can control the open state by using the `open` prop or the `v-model:open` directive. On desktop it controls the expanded/collapsed state, on mobile it opens/closes the sheet menu.

```vue [SidebarOpenExample.vue]
<script setup lang="ts">
import type { NavigationMenuItem } from '@bitrix24/b24ui-nuxt'
import HomeIcon from '@bitrix24/b24icons-vue/outline/HomeIcon'
import MailIcon from '@bitrix24/b24icons-vue/outline/MailIcon'
import ContactIcon from '@bitrix24/b24icons-vue/outline/ContactIcon'
import OpenChatIcon from '@bitrix24/b24icons-vue/outline/OpenChatIcon'

const open = ref(true)

defineShortcuts({
  o: () => open.value = !open.value
})

const items: NavigationMenuItem[] = [
  {
    label: 'Home',
    icon: HomeIcon,
    active: true
  },
  {
    label: 'Inbox',
    icon: MailIcon,
    badge: '4'
  },
  {
    label: 'Contacts',
    icon: ContactIcon
  }
]
</script>

<template>
  <div class="flex flex-1">
    <B24Sidebar v-model:open="open" title="Navigation" collapsible="icon">
      <B24NavigationMenu
        :items="items"
        orientation="vertical"
        :b24ui="{ link: 'p-1.5 overflow-hidden' }"
      />
    </B24Sidebar>

    <div class="flex-1 flex flex-col">
      <div class="h-(--b24ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
        <B24Button
          :icon="OpenChatIcon"
          color="air-tertiary"
          :aria-label="open ? 'Close sidebar' : 'Open sidebar'"
          @click="open = !open"
        />
      </div>

      <div class="flex-1 p-4">
        <Placeholder class="size-full" />
      </div>
    </div>
  </div>
</template>
```

> [!NOTE]
> In this example, leveraging [`defineShortcuts`](/docs/composables/define-shortcuts/), you can toggle the open state of the Sidebar by pressing .

### Persist open state

Use [`useLocalStorage`](https://vueuse.org/core/useLocalStorage/) from VueUse or [`useCookie`](https://nuxt.com/docs/4.x/api/composables/use-cookie) instead of `ref` to persist the sidebar state across page reloads.

```vue [SidebarPersistExample.vue]
<script setup lang="ts">
import type { NavigationMenuItem } from '@bitrix24/b24ui-nuxt'
import { useLocalStorage } from '@vueuse/core'
import HomeIcon from '@bitrix24/b24icons-vue/outline/HomeIcon'
import MailIcon from '@bitrix24/b24icons-vue/outline/MailIcon'
import ContactIcon from '@bitrix24/b24icons-vue/outline/ContactIcon'
import OpenChatIcon from '@bitrix24/b24icons-vue/outline/OpenChatIcon'

const open = useLocalStorage('sidebar-open', true)

defineShortcuts({
  o: () => open.value = !open.value
})

const items: NavigationMenuItem[] = [
  {
    label: 'Home',
    icon: HomeIcon,
    active: true
  },
  {
    label: 'Inbox',
    icon: MailIcon,
    badge: '4'
  },
  {
    label: 'Contacts',
    icon: ContactIcon
  }
]
</script>

<template>
  <div class="flex flex-1">
    <B24Sidebar v-model:open="open" title="Navigation" collapsible="icon">
      <B24NavigationMenu
        :items="items"
        orientation="vertical"
        :b24ui="{ link: 'p-1.5 overflow-hidden' }"
      />
    </B24Sidebar>

    <div class="flex-1 flex flex-col">
      <div class="h-(--b24ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
        <B24Button
          :icon="OpenChatIcon"
          color="air-tertiary"
          aria-label="Toggle sidebar"
          @click="open = !open"
        />
      </div>

      <div class="flex-1 p-4">
        <Placeholder class="size-full" />
      </div>
    </div>
  </div>
</template>
```

> [!NOTE]
> The only difference with the previous example is replacing `ref(true)` with `useLocalStorage('sidebar-open', true)`.

### With custom width

The sidebar width is controlled by the `--sidebar-width` CSS variable (defaults to `16rem`). The collapsed icon width is controlled by `--sidebar-width-icon` (defaults to `4rem`).

Override them globally in your CSS or per-instance with the `style` attribute.

```vue [SidebarWidthExample.vue]
<script setup lang="ts">
import type { NavigationMenuItem } from '@bitrix24/b24ui-nuxt'
import HomeIcon from '@bitrix24/b24icons-vue/outline/HomeIcon'
import MailIcon from '@bitrix24/b24icons-vue/outline/MailIcon'
import ContactIcon from '@bitrix24/b24icons-vue/outline/ContactIcon'
import OpenChatIcon from '@bitrix24/b24icons-vue/outline/OpenChatIcon'

const open = ref(true)

const items: NavigationMenuItem[] = [
  {
    label: 'Home',
    icon: HomeIcon,
    active: true
  },
  {
    label: 'Inbox',
    icon: MailIcon,
    badge: '4'
  },
  {
    label: 'Contacts',
    icon: ContactIcon
  }
]
</script>

<template>
  <div class="flex flex-1">
    <B24Sidebar
      v-model:open="open"
      collapsible="icon"
      :style="{ '--sidebar-width': '20rem' }"
    >
      <B24NavigationMenu
        :items="items"
        orientation="vertical"
        :b24ui="{ link: 'p-1.5 overflow-hidden' }"
      />
    </B24Sidebar>

    <div class="flex-1 flex flex-col">
      <div class="h-(--b24ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
        <B24Button
          :icon="OpenChatIcon"
          color="air-tertiary"
          aria-label="Toggle sidebar"
          @click="open = !open"
        />
      </div>

      <div class="flex-1 p-4">
        <Placeholder class="size-full" />
      </div>
    </div>
  </div>
</template>
```

### With header

To position the sidebar below a [Header](https://bitrix24.github.io/b24ui/raw/docs/components/header.md), customize the `gap` and `container` using the `b24ui` prop.

```vue [SidebarHeaderExample.vue]
<script setup lang="ts">
import type { NavigationMenuItem } from '@bitrix24/b24ui-nuxt'
import HomeIcon from '@bitrix24/b24icons-vue/outline/HomeIcon'
import MailIcon from '@bitrix24/b24icons-vue/outline/MailIcon'
import ContactIcon from '@bitrix24/b24icons-vue/outline/ContactIcon'
import HamburgerMenuIcon from '@bitrix24/b24icons-vue/outline/HamburgerMenuIcon'

const open = ref(true)

const items: NavigationMenuItem[] = [
  {
    label: 'Home',
    icon: HomeIcon,
    active: true
  },
  {
    label: 'Inbox',
    icon: MailIcon,
    badge: '4'
  },
  {
    label: 'Contacts',
    icon: ContactIcon
  }
]
</script>

<template>
  <div class="flex flex-col flex-1">
    <B24Header title="Title" toggle-side="left" :b24ui="{ container: 'px-4!' }">
      <template #toggle>
        <B24Button
          :icon="HamburgerMenuIcon"
          color="air-tertiary"
          aria-label="Toggle sidebar"
          @click="open = !open"
        />
      </template>
    </B24Header>

    <div class="flex flex-1 min-h-0">
      <B24Sidebar
        v-model:open="open"
        collapsible="icon"
        :b24ui="{
          gap: 'h-[calc(100%-var(--b24ui-header-height))]',
          container: 'absolute top-(--b24ui-header-height) bottom-0 h-[calc(100%-var(--b24ui-header-height))]'
        }"
      >
        <B24NavigationMenu
          :items="items"
          orientation="vertical"
          :b24ui="{ link: 'p-1.5 overflow-hidden' }"
        />
      </B24Sidebar>

      <div class="flex-1 p-4">
        <Placeholder class="size-full" />
      </div>
    </div>
  </div>
</template>
```

> [!NOTE]
> The `--b24ui-header-height` variable defaults to `3.625rem` and is used by the Header. Adjust it if your navbar uses a different height.

### With AI chat

Use the sidebar on the right side with [ChatMessages](https://bitrix24.github.io/b24ui/raw/docs/components/chat-messages.md) and [ChatPrompt](https://bitrix24.github.io/b24ui/raw/docs/components/chat-prompt.md) to create an AI chat panel.

```vue [SidebarChatExample.vue]
<script setup lang="ts">
import type { UIMessage } from 'ai'
import { DefaultChatTransport, isTextUIPart } from 'ai'
import { Chat } from '@ai-sdk/vue'
import OpenChatIcon from '@bitrix24/b24icons-vue/outline/OpenChatIcon'

const config = useRuntimeConfig()
const open = ref(true)
const input = ref('')

const messages: UIMessage[] = [
  {
    id: '1',
    role: 'user',
    parts: [{ type: 'text', text: 'What is Bitrix24 UI?' }]
  },
  {
    id: '2',
    role: 'assistant',
    parts: [{ type: 'text', text: 'Bitrix24 UI is a Vue component library built on Reka UI, Tailwind CSS, and Tailwind Variants. It provides 125+ accessible components for building modern web apps.' }]
  }
]

const chat = new Chat({
  messages,
  transport: new DefaultChatTransport({
    api: `${config.public.baseUrl}/api/chat`
  })
})

function onSubmit() {
  if (!input.value.trim()) return

  chat.sendMessage({ text: input.value })

  input.value = ''
}

const b24ui = {
  prose: {
    p: { base: 'my-2 text-sm/6' },
    li: { base: 'my-0.5 text-sm/6' },
    ul: { base: 'my-2' },
    ol: { base: 'my-2' },
    h1: { base: 'text-xl mb-4' },
    h2: { base: 'text-lg mt-6 mb-3' },
    h3: { base: 'text-base mt-4 mb-2' },
    h4: { base: 'text-sm mt-3 mb-1.5' },
    code: { base: 'text-xs' },
    pre: { root: 'my-2', base: 'text-xs/5' },
    table: { root: 'my-2' },
    hr: { base: 'my-4' }
  }
}
</script>

<template>
  <div class="flex flex-1">
    <div class="flex-1 flex flex-col">
      <div class="h-(--b24ui-header-height) shrink-0 flex items-center justify-end px-4 border-b border-default">
        <B24Button
          :icon="OpenChatIcon"
          color="air-tertiary"
          aria-label="Toggle sidebar"
          @click="open = !open"
        />
      </div>

      <div class="flex-1 p-4">
        <Placeholder class="size-full" />
      </div>
    </div>

    <B24Sidebar
      v-model:open="open"
      side="right"
      title="AI Chat"
      close
      :style="{ '--sidebar-width': '20rem' }"
      :b24ui="{ container: 'h-full' }"
    >
      <B24Theme :b24ui="b24ui">
        <B24ChatMessages
          :messages="chat.messages"
          :status="chat.status"
          compact
          class="px-0"
        >
          <template #content="{ message }">
            <template v-for="(part, index) in message.parts" :key="`${message.id}-${part.type}-${index}`">
              <template v-if="isTextUIPart(part)">
                <MDC
                  v-if="message.role === 'assistant'"
                  :value="part.text"
                  :cache-key="`${message.id}-${index}`"
                  class="*:first:mt-0 *:last:mb-0"
                />
                <p v-else-if="message.role === 'user'" class="whitespace-pre-wrap text-sm/6">
                  {{ part.text }}
                </p>
              </template>
            </template>
          </template>
        </B24ChatMessages>
      </B24Theme>

      <template #footer>
        <B24ChatPrompt
          v-model="input"
          :error="chat.error"
          :autofocus="false"
          variant="outline"
          size="sm"
          :b24ui="{ base: 'px-0' }"
          @submit="onSubmit"
        >
          <B24ChatPromptSubmit
            size="sm"
            :status="chat.status"
            @stop="chat.stop()"
            @reload="chat.regenerate()"
          />
        </B24ChatPrompt>
      </template>
    </B24Sidebar>
  </div>
</template>
```

## API

### Props

```ts
/**
 * Props for the Sidebar component
 */
interface SidebarProps {
  /**
   * The element or component this component should render as.
   * @default "\"aside\""
   */
  as?: any;
  /**
   * The visual variant of the sidebar.
   * @default "\"sidebar\""
   */
  variant?: "floating" | "sidebar" | "inset" | undefined;
  /**
   * The collapse behavior of the sidebar.
   * - `offcanvas`: The sidebar slides out of view completely.
   * - `icon`: The sidebar shrinks to icon-only width.
   * - `none`: The sidebar is not collapsible.
   * @default "\"offcanvas\""
   */
  collapsible?: "offcanvas" | "icon" | "none" | undefined;
  /**
   * The side to render the sidebar on.
   * @default "\"left\""
   */
  side?: "left" | "right" | undefined;
  /**
   * The title displayed in the sidebar header.
   */
  title?: string | undefined;
  /**
   * The description displayed in the sidebar header.
   */
  description?: string | undefined;
  /**
   * Display a close button to collapse the sidebar.
   * Only renders when `collapsible` is not `none`.
   * `{ size: 'md', color: 'neutral', variant: 'ghost' }`
   * @default "false"
   */
  close?: boolean | Omit<ButtonProps, LinkPropsKeys> | undefined;
  /**
   * The icon displayed in the close button.
   */
  closeIcon?: IconComponent | undefined;
  /**
   * Display a rail on the sidebar edge to toggle collapse.
   * Only renders when `collapsible` is not `none`.
   * @default "false"
   */
  rail?: boolean | undefined;
  /**
   * The mode of the sidebar menu on mobile.
   * @default "\"slideover\" as never"
   */
  mode?: T | undefined;
  /**
   * The props for the sidebar menu component on mobile.
   */
  menu?: SidebarMenu<T> | undefined;
  b24ui?: { root?: ClassNameValue; gap?: ClassNameValue; container?: ClassNameValue; inner?: ClassNameValue; header?: ClassNameValue; wrapper?: ClassNameValue; title?: ClassNameValue; description?: ClassNameValue; actions?: ClassNameValue; close?: ClassNameValue; body?: ClassNameValue; footer?: ClassNameValue; rail?: ClassNameValue; content?: ClassNameValue; overlay?: ClassNameValue; } | undefined;
  /**
   * @default "true"
   */
  open?: boolean | undefined;
}
```

### Slots

```ts
/**
 * Slots for the Sidebar component
 */
interface SidebarSlots {
  header(): any;
  title(): any;
  description(): any;
  actions(): any;
  close(): any;
  default(): any;
  footer(): any;
  rail(): any;
  content(): any;
}
```

## Theme

```ts [app.config.ts]
export default defineAppConfig({
  b24ui: {
    sidebar: {
      slots: {
        root: 'peer [--sidebar-width:15rem] [--sidebar-width-icon:4.5rem]',
        gap: 'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear',
        container: 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear lg:flex',
        inner: 'flex size-full flex-col overflow-hidden divide-y divide-default',
        header: 'flex items-center gap-1.5 overflow-hidden px-4 min-h-(--b24ui-header-height)',
        wrapper: 'min-w-0 flex-1',
        title: 'text-label font-semibold truncate',
        description: 'text-muted text-sm truncate',
        actions: 'flex items-center gap-1.5 shrink-0',
        close: '',
        body: 'flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4',
        footer: 'flex items-center gap-1.5 overflow-hidden p-4',
        rail: 'absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-px lg:flex hover:after:bg-(--ui-color-divider-accent) after:transition-colors',
        content: '',
        overlay: ''
      },
      variants: {
        side: {
          left: {
            container: 'left-0 border-e border-default',
            rail: 'end-0 translate-x-1/2'
          },
          right: {
            container: 'right-0 border-s border-default',
            rail: '-start-px -translate-x-1/2'
          }
        },
        collapsible: {
          offcanvas: {
            root: 'group/sidebar hidden lg:block',
            gap: 'data-[state=collapsed]:w-0'
          },
          icon: {
            root: 'group/sidebar hidden lg:block',
            gap: 'data-[state=collapsed]:w-(--sidebar-width-icon)',
            container: 'data-[state=collapsed]:w-(--sidebar-width-icon)',
            actions: 'group-data-[state=collapsed]/sidebar:hidden',
            body: 'group-data-[state=collapsed]/sidebar:overflow-hidden'
          },
          none: {
            root: 'h-full w-(--sidebar-width)'
          }
        },
        variant: {
          sidebar: {},
          floating: {
            container: 'p-4 border-transparent',
            inner: 'rounded-lg ring ring-default shadow-lg',
            rail: 'inset-y-4'
          },
          inset: {
            container: 'py-4 border-transparent',
            inner: 'divide-transparent',
            rail: 'inset-y-4'
          }
        }
      },
      compoundVariants: [
        {
          side: 'left',
          collapsible: [
            'offcanvas',
            'icon'
          ],
          class: {
            rail: 'cursor-w-resize data-[state=collapsed]:cursor-e-resize'
          }
        },
        {
          side: 'right',
          collapsible: [
            'offcanvas',
            'icon'
          ],
          class: {
            rail: 'cursor-e-resize data-[state=collapsed]:cursor-w-resize'
          }
        },
        {
          side: 'left',
          collapsible: 'none',
          class: {
            root: 'border-e border-default'
          }
        },
        {
          side: 'right',
          collapsible: 'none',
          class: {
            root: 'border-s border-default'
          }
        },
        {
          side: 'left',
          collapsible: 'offcanvas',
          class: {
            container: 'data-[state=collapsed]:-left-(--sidebar-width)'
          }
        },
        {
          side: 'right',
          collapsible: 'offcanvas',
          class: {
            container: 'data-[state=collapsed]:-right-(--sidebar-width)'
          }
        },
        {
          variant: 'floating',
          collapsible: 'icon',
          class: {
            gap: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+--spacing(6.5))]',
            container: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+--spacing(6.5)+2px)]'
          }
        },
        {
          variant: 'floating',
          collapsible: 'none',
          class: {
            root: 'p-4 border-0'
          }
        },
        {
          variant: 'inset',
          collapsible: 'none',
          class: {
            root: 'py-4 border-0'
          }
        },
        {
          variant: 'floating',
          side: 'left',
          class: {
            rail: 'end-4'
          }
        },
        {
          variant: 'floating',
          side: 'right',
          class: {
            rail: 'start-[calc(--spacing(4)-1px)]'
          }
        }
      ]
    }
  }
})
```