---
title: "PageCardGroup"
description: "A selectable group of PageCard items. Single or multi-select with optional grouping by category."
canonical_url: "https://bitrix24.github.io/b24ui/docs/components/page-card-group"
last_updated: "2026-05-13"
---
# PageCardGroup

> A selectable group of PageCard items. Single or multi-select with optional grouping by category.

## Usage

`PageCardGroup` renders a responsive grid of selectable [PageCard](https://bitrix24.github.io/b24ui/raw/docs/components/page-card.md) items. The selected card gets a highlighted ring and a corner badge. Items can be grouped into category sections.

```vue [PageCardGroupExample.vue]
<script setup lang="ts">
import TelephonyHandset1Icon from '@bitrix24/b24icons-vue/main/TelephonyHandset1Icon'
import MailIcon from '@bitrix24/b24icons-vue/main/MailIcon'
import FeedbackIcon from '@bitrix24/b24icons-vue/main/FeedbackIcon'
import DocumentStreamIcon from '@bitrix24/b24icons-vue/main/DocumentStreamIcon'
import CartWithCursorIcon from '@bitrix24/b24icons-vue/main/CartWithCursorIcon'
import CreditDebitCardIcon from '@bitrix24/b24icons-vue/main/CreditDebitCardIcon'
import PictureIcon from '@bitrix24/b24icons-vue/main/PictureIcon'

const items = [
  { value: 'callback', category: 'Customer communications', icon: TelephonyHandset1Icon, label: 'Callback', description: 'Capture the phone number and start a callback' },
  { value: 'contacts', category: 'Customer communications', icon: MailIcon, label: 'Contact details', description: 'Collect phone, email and client name' },
  { value: 'feedback', category: 'Customer communications', icon: FeedbackIcon, label: 'Feedback', description: 'Gather customer reviews' },
  { value: 'multi', category: 'Customer communications', icon: DocumentStreamIcon, label: 'Multi-page form', description: 'Split a long form across several pages' },
  { value: 'no-image', category: 'Sales', icon: CartWithCursorIcon, label: 'Products without images', description: 'Order a product without a picture' },
  { value: 'pay', category: 'Sales', icon: CreditDebitCardIcon, label: 'Products with payment', description: 'Pay for a product right from the form' },
  { value: 'small-img', category: 'Sales', icon: PictureIcon, label: 'Products with small photo', description: 'Order a product with a small thumbnail preview' }
]

const value = ref('contacts')
</script>

<template>
  <B24PageCardGroup v-model="value" :items="items" />
</template>
```

> [!TIP]
> Use `PageCardGroup` for picking one option (or several) out of a small set of presented choices. For plain checkbox/radio lists prefer [CheckboxGroup](https://bitrix24.github.io/b24ui/raw/docs/components/checkbox-group.md) or [RadioGroup](https://bitrix24.github.io/b24ui/raw/docs/components/radio-group.md).

### Items

Pass an array of objects via the `items` prop. Each item supports `label`, `description`, `icon`, `value`, `disabled` and an optional `category` for grouping. Customize the field names with `value-key`, `label-key`, `description-key`, `icon-key`, `category-key`.

### Multiple

Set the `multiple` prop to switch to multi-select mode. The `v-model` binds to an array of values.

```vue
<B24PageCardGroup v-model="picks" :items="items" multiple />
```

### Columns

Use the `columns` prop (`'1'`, `'2'`, `'3'`, `'4'`) to set the maximum number of columns on desktop. The grid is always responsive — 1 column on mobile, 2 on tablet, then up to `columns` on `lg+`.

### Size

Use the `size` prop (`'sm'`, `'md'`, `'lg'`) to scale the icon circle, the inner icon, the inner gap, and the title/description font sizes. Defaults to `'md'`. The corner badge size is derived from `size` unless `badge-size` is set explicitly.

### Variant

The `variant` prop is forwarded to each inner [PageCard](https://bitrix24.github.io/b24ui/raw/docs/components/page-card.md) and controls its base style. See PageCard variants for the full list.

### Color

The `color` prop is the umbrella accent — it drives both the highlighted border on the selected card and the corner badge color. Defaults to `air-primary-success`.

```vue
<B24PageCardGroup v-model="value" :items="items" color="air-primary" />
```

### Highlight color

Override the umbrella `color` for the card border only with `highlight-color`. Falls back to `color` when not set.

### Badge color

Override the umbrella `color` for the corner [Badge](https://bitrix24.github.io/b24ui/raw/docs/components/badge.md) only with `badge-color`. Falls back to `color` when not set.

### Badge size

Override the badge size derived from `size` with `badge-size`.

### Icon vs Avatar

Each item picks one of two leading visuals:

- `icon` field — plain icon, no circle. Theme controls size via `leadingIcon`. Use this for compact, non-decorated lists.
- `avatar` field — [B24Avatar](https://bitrix24.github.io/b24ui/raw/docs/components/avatar.md) with full color control (`color`, `src`, `chip`, `icon`). Avatar size is derived from the group `size` (`sm` → `lg`, `md` → `xl`, `lg` → `2xl`) and the avatar's `alt` falls back to the item's `label`.

`icon` wins when both are set on the same item — the plain icon renders and `avatar` is ignored. Pick one per item:

```ts
// shows the icon via B24Avatar (color circle around the icon)
{ avatar: { color: 'air-primary-warning', icon: FeedbackIcon }, label: 'Feedback' }

// shows the plain icon only — `icon` at the top level takes precedence
{ icon: FeedbackIcon, avatar: { color: 'air-primary-warning' }, label: 'Feedback' }
```

Set a group-level Avatar default that every avatar-mode item inherits:

```vue
<B24PageCardGroup v-model="value" :items="items" :avatar="{ color: 'air-secondary' }" />
```

Per-item `avatar` config merges on top of the group default:

```vue [PageCardGroupAvatarExample.vue]
<script setup lang="ts">
import TelephonyHandset1Icon from '@bitrix24/b24icons-vue/main/TelephonyHandset1Icon'
import MailIcon from '@bitrix24/b24icons-vue/main/MailIcon'
import FeedbackIcon from '@bitrix24/b24icons-vue/main/FeedbackIcon'
import type { PageCardGroupItem } from '@bitrix24/b24ui-nuxt'

const items: PageCardGroupItem[] = [
  {
    value: 'callback',
    avatar: { color: 'air-primary-success', icon: TelephonyHandset1Icon },
    label: 'Callback',
    description: 'Capture the phone number and start a callback'
  },
  {
    value: 'contacts',
    avatar: { color: 'air-primary-copilot', icon: MailIcon },
    label: 'Contact details',
    description: 'Collect phone, email and client name'
  },
  {
    value: 'feedback',
    avatar: { color: 'air-primary-warning', icon: FeedbackIcon },
    label: 'Feedback',
    description: 'Gather customer reviews'
  }
]

const value = ref('contacts')
</script>

<template>
  <B24PageCardGroup
    v-model="value"
    :items="items"
    color="air-primary-copilot"
    columns="3"
    category-key=""
  />
</template>
```

The `avatar` field accepts the full [B24Avatar](https://bitrix24.github.io/b24ui/raw/docs/components/avatar.md) API — same hook drives `src` (photo), `chip`, or a different `icon` per item.

### Category grouping

When items contain a `category` field (or another field set via `category-key`), the group renders one section per category with a heading above each grid. Pass `category-key=""` to disable grouping.

## Prompt

> Prompt: Render a payment-methods picker on the settings page.
> On a settings page, render the list of payment methods as a grid of cards. Each card carries an icon and a short description. The user can enable several methods at once.
> Requirements:
> - Use `B24PageCardGroup` with `multiple` and bind `v-model` to a `ref<string[]>`
> - Each item exposes `value`, `label`, `description` and `icon` (an icon component imported from `@bitrix24/b24icons-vue`)
> - Keep the umbrella accent in sync — set `color` on the group instead of `highlight-color` / `badge-color` separately
> - Wrap the group in `B24FormField` with a label so it integrates with the surrounding settings form
> - Pick a sensible `size` (`md` by default) and `columns` (`2` or `3`) for the layout

## Examples

### Click / change handler

Listen to the `change` event to react to user selections. The event fires after `v-model` updates and receives the native `change` event.

```vue
<script setup lang="ts">
const value = ref('callback')
function onChange(event: Event) {
  console.log('selected', value.value, event)
}
</script>

<template>
  <B24PageCardGroup v-model="value" :items="items" @change="onChange" />
</template>
```

### Disable an item

Set `disabled: true` on an individual item to lock it out without removing it from the grid.

```ts
const items = [
  { value: 'callback', label: 'Callback', icon: PhoneIcon },
  { value: 'pay', label: 'Payment', icon: CardIcon, disabled: true }
]
```

### Custom badge

Override the corner badge through the `badge` slot. Receives `{ item, selected }`.

```vue
<B24PageCardGroup v-model="value" :items="items">
  <template #badge="{ selected }">
    <B24Badge v-if="selected" label="Picked" color="air-primary-success" size="sm" />
  </template>
</B24PageCardGroup>
```

### Custom leading icon

The leading slot is an escape hatch for cases where the built-in [B24Avatar](https://bitrix24.github.io/b24ui/raw/docs/components/avatar.md) (driven by the `avatar` prop / item `avatar` field) isn't flexible enough. Override the icon circle through the `leading` slot — receives `{ item, selected }`.

```vue
<B24PageCardGroup v-model="value" :items="items">
  <template #leading="{ item, selected }">
    <B24Avatar
      :alt="item.label"
      :icon="item.icon"
      :color="selected ? 'air-primary' : 'air-secondary-no-accent'"
    />
  </template>
</B24PageCardGroup>
```

### Custom category label

Override category headings through the `categoryLabel` slot. Receives `{ category, items }`.

```vue
<B24PageCardGroup v-model="value" :items="items">
  <template #categoryLabel="{ category, items }">
    <h3 class="text-(length:--ui-font-size-lg) font-(--ui-font-weight-semi-bold)">
      {{ category }} <span class="text-(--ui-color-design-outline-content-secondary)">({{ items.length }})</span>
    </h3>
  </template>
</B24PageCardGroup>
```

## API

### Props

```ts
/**
 * Props for the PageCardGroup component
 */
interface PageCardGroupProps {
  /**
   * The element or component this component should render as.
   * @default "\"div\""
   */
  as?: any;
  legend?: string | undefined;
  items?: PageCardGroupItem[] | undefined;
  /**
   * The controlled value. Can be bound with `v-model`. Single value when `multiple` is false, array otherwise.
   */
  modelValue?: AcceptableValue | AcceptableValue[] | undefined;
  /**
   * The initial value when uncontrolled.
   */
  defaultValue?: AcceptableValue | AcceptableValue[] | undefined;
  /**
   * @default "\"value\""
   */
  valueKey?: string | undefined;
  /**
   * @default "\"label\""
   */
  labelKey?: string | undefined;
  /**
   * @default "\"description\""
   */
  descriptionKey?: string | undefined;
  /**
   * @default "\"icon\""
   */
  iconKey?: string | undefined;
  /**
   * Item field used to group cards into sections. Set to an empty string to disable grouping.
   * @default "\"category\""
   */
  categoryKey?: string | undefined;
  /**
   * Switch to multi-select mode (checkbox semantics, value is an array).
   * @default "false"
   */
  multiple?: boolean | undefined;
  disabled?: boolean | undefined;
  required?: boolean | undefined;
  name?: string | undefined;
  /**
   * Card size — drives the inner avatar size, inner gap and title/description text size.
   * @default "\"md\""
   */
  size?: "md" | "sm" | "lg" | undefined;
  /**
   * Variant forwarded to each `PageCard`.
   * @default "\"outline\""
   */
  variant?: "outline" | "filled" | "filled-success" | "filled-alert" | "filled-warning" | "filled-copilot" | "filled-no-accent" | "filled-black" | "tinted" | "tinted-alt" | "tinted-accent-1" | "tinted-success" | "tinted-alert" | "tinted-warning" | "tinted-no-accent" | "outline-alt" | "outline-accent" | "outline-accent-2" | "outline-success" | "outline-alert" | "outline-warning" | "outline-copilot" | "outline-no-accent" | "plain" | "plain-accent" | "plain-no-accent" | "selection" | undefined;
  /**
   * Umbrella accent color. Drives both the highlighted card border and the corner badge
   * unless `highlightColor` / `badgeColor` are set explicitly.
   * @default "\"air-primary-success\""
   */
  color?: "air-primary" | "air-primary-success" | "air-primary-alert" | "air-primary-warning" | "air-primary-copilot";
  /**
   * Highlight color forwarded to the selected `PageCard`. Falls back to `color` when not set.
   */
  highlightColor?: "air-primary-success" | "air-primary" | "air-primary-alert" | "air-primary-copilot" | "air-primary-warning" | undefined;
  /**
   * Color of the corner badge shown on the selected card. Falls back to `color` when not set.
   */
  badgeColor?: "default" | "air-primary-success" | "air-primary" | "air-primary-alert" | "air-primary-copilot" | "air-primary-warning" | "air-secondary" | "air-secondary-alert" | "air-secondary-accent" | "air-secondary-accent-1" | "air-secondary-accent-2" | "air-tertiary" | "air-selection" | "danger" | "success" | "warning" | "primary" | "secondary" | "collab" | "ai" | undefined;
  /**
   * Size of the corner badge shown on the selected card. Falls back to a value derived
   * from the group `size` when not set.
   */
  badgeSize?: "md" | "sm" | "lg" | "xl" | "xss" | "xs" | undefined;
  /**
   * Group-level Avatar defaults forwarded to every card whose item opts into avatar mode
   * (`item.avatar` set or this prop set). Per-item `avatar` field merges on top.
   */
  avatar?: Partial<AvatarProps> | undefined;
  /**
   * Max columns on desktop.
   */
  columns?: "3" | "1" | "2" | "4" | undefined;
  b24ui?: { root?: ClassNameValue; fieldset?: ClassNameValue; legend?: ClassNameValue; group?: ClassNameValue; categoryLabel?: ClassNameValue; grid?: ClassNameValue; item?: ClassNameValue; card?: ClassNameValue; cardContainer?: ClassNameValue; cardWrapper?: ClassNameValue; cardLeading?: ClassNameValue; cardBody?: ClassNameValue; cardTitle?: ClassNameValue; cardDescription?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; badge?: ClassNameValue; badgeIcon?: ClassNameValue; } | undefined;
}
```

### Slots

```ts
/**
 * Slots for the PageCardGroup component
 */
interface PageCardGroupSlots {
  legend(): any;
  categoryLabel(): any;
  leading(): any;
  badge(): any;
}
```

## Theme

```ts [app.config.ts]
export default defineAppConfig({
  b24ui: {
    pageCardGroup: {
      slots: {
        root: 'flex flex-col gap-8',
        fieldset: 'flex flex-col gap-8 border-0 p-0 m-0 min-w-0',
        legend: 'text-(length:--ui-font-size-2xl)/[normal] font-(--ui-font-weight-semi-bold) mb-2',
        group: 'flex flex-col gap-4',
        categoryLabel: 'text-(length:--ui-font-size-base)/[normal] font-(--ui-font-weight-medium) text-(--ui-color-design-outline-content-secondary)',
        grid: 'grid gap-4',
        item: 'relative flex cursor-pointer transition-shadow duration-200 rounded-lg [&:not([data-disabled])]:hover:shadow-(--ui-shadow-bottom-l) data-[disabled]:cursor-not-allowed',
        card: 'flex-1',
        cardContainer: '',
        cardWrapper: 'flex flex-row items-center',
        cardLeading: 'mb-0 shrink-0',
        cardBody: 'flex-1 min-w-0',
        cardTitle: '',
        cardDescription: '',
        leadingIcon: '',
        leadingAvatar: '',
        leadingAvatarSize: '',
        badge: 'absolute -top-2 -end-2 z-10',
        badgeIcon: 'rounded-full'
      },
      variants: {
        size: {
          sm: {
            cardWrapper: 'gap-2.5',
            cardTitle: 'text-(length:--ui-font-size-base)/[normal]',
            cardDescription: 'text-(length:--ui-font-size-sm)/[normal]',
            leadingIcon: 'size-7 shrink-0',
            leadingAvatarSize: 'sm'
          },
          md: {
            cardWrapper: 'gap-3',
            cardTitle: 'text-(length:--ui-font-size-lg)/[normal]',
            cardDescription: 'text-(length:--ui-font-size-base)/[normal]',
            leadingIcon: 'size-8 shrink-0',
            leadingAvatarSize: 'md'
          },
          lg: {
            cardWrapper: 'gap-4',
            cardTitle: 'text-(length:--ui-font-size-xl)/[normal]',
            cardDescription: 'text-(length:--ui-font-size-base)/[normal]',
            leadingIcon: 'size-10 shrink-0',
            leadingAvatarSize: 'lg'
          }
        },
        columns: {
          '1': {
            grid: 'grid-cols-1'
          },
          '2': {
            grid: 'grid-cols-1 sm:grid-cols-2'
          },
          '3': {
            grid: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3'
          },
          '4': {
            grid: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4'
          }
        },
        disabled: {
          true: {
            root: 'opacity-30 pointer-events-none'
          }
        }
      },
      defaultVariants: {
        size: 'md',
        columns: '3'
      }
    }
  }
})
```

## Sitemap

See the full [sitemap](/b24ui/sitemap.md) for all pages.
