---
name: item
title: Item
url: https://ui.frontic.com/components/ui/item.md
tier: ui
install: npx @frontic/ui add item
files:
  - components/ui/item/Item.vue
  - components/ui/item/ItemActions.vue
  - components/ui/item/ItemContent.vue
  - components/ui/item/ItemDescription.vue
  - components/ui/item/ItemFooter.vue
  - components/ui/item/ItemGroup.vue
  - components/ui/item/ItemHeader.vue
  - components/ui/item/ItemMedia.vue
  - components/ui/item/ItemSeparator.vue
  - components/ui/item/ItemTitle.vue
  - components/ui/item/index.ts
registryDependencies:
  - separator
dependencies:
  - class-variance-authority
  - reka-ui
---

# Item

Groups the media, text and actions of one entry, such as a list row or a link to a page.

## Install

```sh
npx @frontic/ui add item
```

Built from: [Separator](https://ui.frontic.com/components/ui/separator.md)

Used by: [Account Access](https://ui.frontic.com/components/sections/account-access.md), [Cart Summary](https://ui.frontic.com/components/sections/cart-summary.md), [Team Members](https://ui.frontic.com/components/sections/team-members.md)

npm packages: `class-variance-authority`, `reka-ui`

## Examples

```vue
<script setup lang="ts">
import { PackageIcon, ChevronRightIcon } from '@lucide/vue'
import { Item, ItemActions, ItemContent, ItemDescription, ItemMedia, ItemTitle } from '@/components/ui/item'
</script>

<template>
  <Item as="a" href="#returns" variant="outline" class="max-w-md">
    <ItemMedia variant="icon">
      <PackageIcon data-icon />
    </ItemMedia>
    <ItemContent>
      <ItemTitle>Free returns</ItemTitle>
      <ItemDescription>Send anything back within 30 days.</ItemDescription>
    </ItemContent>
    <ItemActions>
      <ChevronRightIcon class="size-4 rtl:-scale-x-100" data-icon />
    </ItemActions>
  </Item>
</template>
```

## Anatomy

```vue
<Item>
  <ItemMedia />
  <ItemContent>
    <ItemTitle />
    <ItemDescription />
  </ItemContent>
  <ItemActions />
</Item>
```

## API reference

### Item

Groups the media, text and actions of one entry, such as a list row. Renders a `<div>`.

`data-slot="item"` · `Item.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `variant` | `'default' \| 'outline' \| 'muted'` | `'default'` | The visual style: no border, an outlined border or a muted background. |
| `size` | `'md' \| 'xs' \| 'sm'` | `'md'` | The padding and gap around the item's content. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-size` | `'md' \| 'xs' \| 'sm'` | Carries the `size` prop. |
| `data-variant` | `'default' \| 'outline' \| 'muted'` | Carries the `variant` prop. |

### ItemActions

Holds the actions of its item, such as buttons or links. Renders a `<div>`.

`data-slot="item-actions"` · `ItemActions.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### ItemContent

Groups the title and description of its item. Renders a `<div>`.

`data-slot="item-content"` · `ItemContent.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### ItemDescription

Gives more detail about its item below the title. Renders a `<p>`.

`data-slot="item-description"` · `ItemDescription.vue`

Styling: `item-description`, which extends [`part-description`](https://ui.frontic.com/DESIGN.md#part-description) and [`link-primary`](https://ui.frontic.com/DESIGN.md#link-primary). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'p'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### ItemFooter

Holds extra content below the main row of its item. Renders a `<div>`.

`data-slot="item-footer"` · `ItemFooter.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### ItemGroup

Groups several items into one list. Renders a `<div>`.

`data-slot="item-group"` · `ItemGroup.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### ItemHeader

Holds extra content above the main row of its item. Renders a `<div>`.

`data-slot="item-header"` · `ItemHeader.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### ItemMedia

Shows an icon, image or avatar at the start of its item. Renders a `<div>`.

`data-slot="item-media"` · `ItemMedia.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `variant` | `ItemMediaVariant`: `'icon' \| 'default' \| 'image'` | `'default'` | How the media is framed: as is, sized as an icon, or as a square image. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-variant` | `'icon' \| 'default' \| 'image'` | Carries the `variant` prop. |

### ItemSeparator

Separates the items of a group.

`data-slot="item-separator"` · `ItemSeparator.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `orientation` | `DataOrientation`: `'vertical' \| 'horizontal'` |  | Orientation of the component.<br><br>Either `vertical` or `horizontal`. Defaults to `horizontal`. |
| `decorative` | `boolean` | `false` | Whether or not the component is purely decorative.<br><br>When `true`, accessibility-related attributes are updated so that that the rendered element is removed from the accessibility tree. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### ItemTitle

Names its item. Renders a `<div>`.

`data-slot="item-title"` · `ItemTitle.vue`

Styling: `item-title`, which extends [`part-title`](https://ui.frontic.com/DESIGN.md#part-title). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

## Exports

```ts
import { type ItemMediaVariant } from '@/components/ui/item'
```

- `ItemMediaVariant`: `'default' \| 'icon' \| 'image'`

## Styling

| Part | Recipe |
| --- | --- |
| `separator` | [`item-separator`](https://ui.frontic.com/DESIGN.md#item-separator) |
| `header` | [`item-header`](https://ui.frontic.com/DESIGN.md#item-header) |
| `group` | [`item-group`](https://ui.frontic.com/DESIGN.md#item-group) |
| `footer` | [`item-footer`](https://ui.frontic.com/DESIGN.md#item-footer) |
| `content` | [`item-content`](https://ui.frontic.com/DESIGN.md#item-content) |
| `actions` | [`item-actions`](https://ui.frontic.com/DESIGN.md#item-actions) |
| `default` | [`item`](https://ui.frontic.com/DESIGN.md#item) |
| `link` | [`item-link`](https://ui.frontic.com/DESIGN.md#item-link) |
| `media` | [`item-media`](https://ui.frontic.com/DESIGN.md#item-media) |
| `title` | [`item-title`](https://ui.frontic.com/DESIGN.md#item-title) |
| `description` | [`item-description`](https://ui.frontic.com/DESIGN.md#item-description) |

States with a look of their own: hover and focus.

## Source

`components/ui/item/Item.vue`

```vue
<script lang="ts">
/** Groups the media, text and actions of one entry, such as a list row. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { cva, type VariantProps } from 'class-variance-authority'
import { Primitive } from 'reka-ui'

const item = cva('fui-item group/item transition-colors duration-100 outline-none', {
  variants: {
    variant: {
      default: 'fui-item-default',
      outline: 'fui-item-outline',
      muted: 'fui-item-muted',
    },
    size: {
      md: 'fui-item-md',
      sm: 'fui-item-sm',
      xs: 'fui-item-xs',
    },
  },
  defaultVariants: { variant: 'default', size: 'md' },
})

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
      /** The visual style: no border, an outlined border or a muted background. */
      variant?: VariantProps<typeof item>['variant']
      /** The padding and gap around the item's content. */
      size?: VariantProps<typeof item>['size']
    }
  >(),
  {
    as: 'div',
    variant: 'default',
    size: 'md',
  },
)
</script>

<template>
  <Primitive
    data-slot="item"
    :data-variant="props.variant"
    :data-size="props.size"
    :as="props.as"
    :as-child="props.asChild"
    :class="[item({ variant: props.variant, size: props.size }), (props.as === 'a' || props.asChild) && 'fui-item-link', props.class]"
  >
    <slot />
  </Primitive>
</template>
```

`components/ui/item/ItemActions.vue`

```vue
<script lang="ts">
/** Holds the actions of its item, such as buttons or links. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
    }
  >(),
  { as: 'div' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="item-actions" :class="['fui-item-actions', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/item/ItemContent.vue`

```vue
<script lang="ts">
/** Groups the title and description of its item. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
    }
  >(),
  { as: 'div' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="item-content" :class="['fui-item-content', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/item/ItemDescription.vue`

```vue
<script lang="ts">
/** Gives more detail about its item below the title. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
    }
  >(),
  { as: 'p' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="item-description" :class="['fui-item-description line-clamp-2', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/item/ItemFooter.vue`

```vue
<script lang="ts">
/** Holds extra content below the main row of its item. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
    }
  >(),
  { as: 'div' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="item-footer" :class="['fui-item-footer basis-full', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/item/ItemGroup.vue`

```vue
<script lang="ts">
/** Groups several items into one list. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
    }
  >(),
  { as: 'div' },
)
</script>

<template>
  <Primitive
    :as="props.as"
    :as-child="props.asChild"
    role="list"
    data-slot="item-group"
    :class="['fui-item-group group/item-group has-[[data-size=sm]]:gap-2.5 has-[[data-size=xs]]:gap-2', props.class]"
  >
    <slot />
  </Primitive>
</template>
```

`components/ui/item/ItemHeader.vue`

```vue
<script lang="ts">
/** Holds extra content above the main row of its item. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
    }
  >(),
  { as: 'div' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="item-header" :class="['fui-item-header basis-full', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/item/ItemMedia.vue`

```vue
<script lang="ts">
/** Shows an icon, image or avatar at the start of its item. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import type { ItemMediaVariant } from '.'
import { cva } from 'class-variance-authority'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
      /** How the media is framed: as is, sized as an icon, or as a square image. */
      variant?: ItemMediaVariant
    }
  >(),
  { as: 'div', variant: 'default' },
)

const itemMedia = cva('fui-item-media [&_svg]:pointer-events-none group-has-[[data-slot=item-description]]/item:translate-y-0.5 group-has-[[data-slot=item-description]]/item:self-start', {
  variants: {
    variant: {
      default: 'fui-item-media-default',
      icon: 'fui-item-media-icon',
      image: 'fui-item-media-image',
    },
  },
  defaultVariants: { variant: 'default' },
})
</script>

<template>
  <Primitive
    :as="props.as"
    :as-child="props.asChild"
    data-slot="item-media"
    :data-variant="props.variant"
    :class="[itemMedia({ variant: props.variant }), props.variant === 'image' && 'overflow-hidden [&_img]:size-full [&_img]:object-cover', props.class]"
  >
    <slot />
  </Primitive>
</template>
```

`components/ui/item/ItemSeparator.vue`

```vue
<script lang="ts">
/** Separates the items of a group. */
export default {}
</script>

<script setup lang="ts">
import type { SeparatorProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { Separator } from '@library/components/ui/separator'

const props = defineProps<SeparatorProps & { class?: HTMLAttributes['class'] }>()
</script>

<template>
  <Separator data-slot="item-separator" orientation="horizontal" :class="['fui-item-separator', props.class]" />
</template>
```

`components/ui/item/ItemTitle.vue`

```vue
<script lang="ts">
/** Names its item. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
    }
  >(),
  { as: 'div' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="item-title" :class="['fui-item-title line-clamp-1 flex', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/item/index.ts`

```ts
export { default as Item } from './Item.vue'
export { default as ItemActions } from './ItemActions.vue'
export { default as ItemContent } from './ItemContent.vue'
export { default as ItemDescription } from './ItemDescription.vue'
export { default as ItemFooter } from './ItemFooter.vue'
export { default as ItemGroup } from './ItemGroup.vue'
export { default as ItemHeader } from './ItemHeader.vue'
export { default as ItemMedia } from './ItemMedia.vue'
export { default as ItemSeparator } from './ItemSeparator.vue'
export { default as ItemTitle } from './ItemTitle.vue'

export type ItemMediaVariant = 'default' | 'icon' | 'image'
```
