---
name: table
title: Table
url: https://ui.frontic.com/components/ui/table.md
tier: ui
install: npx @frontic/ui add table
files:
  - components/ui/table/Table.vue
  - components/ui/table/TableBody.vue
  - components/ui/table/TableCaption.vue
  - components/ui/table/TableCell.vue
  - components/ui/table/TableEmpty.vue
  - components/ui/table/TableFooter.vue
  - components/ui/table/TableHead.vue
  - components/ui/table/TableHeader.vue
  - components/ui/table/TableRow.vue
  - components/ui/table/index.ts
dependencies:
  - "@vueuse/core"
  - reka-ui
---

# Table

Rows and columns of data such as orders, with header, footer, caption and an empty state; a wide table scrolls in its box and takes keyboard focus to do so.

## Install

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

Used by: [Recent Orders](https://ui.frontic.com/components/sections/recent-orders.md)

npm packages: `@vueuse/core`, `reka-ui`

## Examples

```vue
<script setup lang="ts">
import { Badge } from '@/components/ui/badge'
import { Price } from '@/components/ui/price'
import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from '@/components/ui/table'

const orders = [
  { id: '#1042', status: 'Shipped', variant: 'info', total: 14900 },
  { id: '#1043', status: 'Processing', variant: 'primary', total: 14500 },
  { id: '#1044', status: 'Delivered', variant: 'success', total: 11200 },
] as const
const sum = { amount: orders.reduce((total, order) => total + order.total, 0), currency: 'EUR' }
</script>

<template>
  <Table>
    <TableCaption>Your recent orders.</TableCaption>
    <TableHeader>
      <TableRow>
        <TableHead>Order</TableHead>
        <TableHead>Status</TableHead>
        <TableHead class="text-end">Total</TableHead>
      </TableRow>
    </TableHeader>
    <TableBody>
      <TableRow v-for="order in orders" :key="order.id">
        <TableCell>{{ order.id }}</TableCell>
        <TableCell
          ><Badge :variant="order.variant" size="sm">{{ order.status }}</Badge></TableCell
        >
        <TableCell class="text-end"><Price align="end" :price="{ amount: order.total, currency: 'EUR' }" size="sm" /></TableCell>
      </TableRow>
    </TableBody>
    <TableFooter>
      <TableRow>
        <TableCell colspan="2">Total</TableCell>
        <TableCell class="text-end"><Price align="end" :price="sum" size="sm" /></TableCell>
      </TableRow>
    </TableFooter>
  </Table>
</template>
```

The example also uses `badge` and `price`, which `add table` leaves out: `npx @frontic/ui add badge price`.

## Anatomy

```vue
<Table>
  <TableCaption />
  <TableHeader>
    <TableRow>
      <TableHead />
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell />
    </TableRow>
  </TableBody>
  <TableFooter>
    <TableRow>
      <TableCell />
    </TableRow>
  </TableFooter>
</Table>
```

## API reference

### Table

Groups all parts of the table, which shows data in rows and columns. Renders a `<table>`.

`data-slot="table"` · `Table.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### TableBody

Holds the data rows of the table. Renders a `<tbody>`.

`data-slot="table-body"` · `TableBody.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` | `'tbody'` | 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. |

### TableCaption

Describes in a short line what the table shows. Renders a `<caption>`.

`data-slot="table-caption"` · `TableCaption.vue`

Styling: `table-caption`, which extends [`hint`](https://ui.frontic.com/DESIGN.md#hint). 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` | `'caption'` | 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. |

### TableCell

Holds one value in a row of the table. Renders a `<td>`.

`data-slot="table-cell"` · `TableCell.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` | `'td'` | 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. |

### TableEmpty

Shows a message in place of rows when the table has no data. Renders a `<td>`.

`data-slot="table-empty"` · `TableEmpty.vue`

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

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `colspan` | `number` | `1` | The number of columns the empty row spans; set it to the table's column count. |

### TableFooter

Holds the summary rows at the bottom of the table, such as totals. Renders a `<tfoot>`.

`data-slot="table-footer"` · `TableFooter.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` | `'tfoot'` | 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. |

### TableHead

Labels one column of the table. Renders a `<th>`.

`data-slot="table-head"` · `TableHead.vue`

Styling: `table-head`, which extends [`label`](https://ui.frontic.com/DESIGN.md#label). 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` | `'th'` | 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. |

### TableHeader

Holds the row of column labels at the top of the table. Renders a `<thead>`.

`data-slot="table-header"` · `TableHeader.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` | `'thead'` | 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. |

### TableRow

Groups the cells that belong to one entry of the table. Renders a `<tr>`.

`data-slot="table-row"` · `TableRow.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` | `'tr'` | 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. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-selected` |  | Present while the item is selected. |

## Styling

| Part | Recipe |
| --- | --- |
| `default` | [`table`](https://ui.frontic.com/DESIGN.md#table) |
| `row` | [`table-row`](https://ui.frontic.com/DESIGN.md#table-row) |
| `head` | [`table-head`](https://ui.frontic.com/DESIGN.md#table-head) |
| `cell` | [`table-cell`](https://ui.frontic.com/DESIGN.md#table-cell) |
| `empty` | [`table-empty`](https://ui.frontic.com/DESIGN.md#table-empty) |
| `footer` | [`table-footer`](https://ui.frontic.com/DESIGN.md#table-footer) |
| `caption` | [`table-caption`](https://ui.frontic.com/DESIGN.md#table-caption) |
| `focus-ring` | [`focus-ring`](https://ui.frontic.com/DESIGN.md#focus-ring) |

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

## Source

`components/ui/table/Table.vue`

```vue
<script lang="ts">
/** Groups all parts of the table, which shows data in rows and columns. */
export default {}
</script>

<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { useResizeObserver } from '@vueuse/core'
import { ref, useTemplateRef } from 'vue'

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

// A table wider than its box can only be scrolled from the keyboard once the box takes focus.
const container = useTemplateRef<HTMLElement>('container')
const scrolls = ref(false)
const name = ref<string>()
useResizeObserver([container, () => container.value?.firstElementChild as HTMLElement | undefined], () => {
  const box = container.value
  if (!box) return
  scrolls.value = box.scrollWidth > box.clientWidth
  name.value = box.querySelector('caption')?.textContent?.trim() || undefined
})
</script>

<template>
  <div
    ref="container"
    data-slot="table-container"
    :tabindex="scrolls ? 0 : undefined"
    :role="scrolls ? 'region' : undefined"
    :aria-label="scrolls ? name : undefined"
    class="relative w-full overflow-x-auto outline-none focus-visible:fui-focus-ring"
  >
    <table data-slot="table" :class="['fui-table caption-bottom', props.class]">
      <slot />
    </table>
  </div>
</template>
```

`components/ui/table/TableBody.vue`

```vue
<script lang="ts">
/** Holds the data rows of the table. */
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: 'tbody' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="table-body" :class="['*:last:border-b-0', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/table/TableCaption.vue`

```vue
<script lang="ts">
/** Describes in a short line what the table shows. */
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: 'caption' },
)
</script>

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

`components/ui/table/TableCell.vue`

```vue
<script lang="ts">
/** Holds one value in a row of the table. */
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: 'td' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="table-cell" :class="['fui-table-cell align-middle whitespace-nowrap has-[[role=checkbox]]:pe-0', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/table/TableEmpty.vue`

```vue
<script lang="ts">
/** Shows a message in place of rows when the table has no data. */
export default {}
</script>

<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import TableRow from './TableRow.vue'

const props = withDefaults(
  defineProps<{
    class?: HTMLAttributes['class']
    /** The number of columns the empty row spans; set it to the table's column count. */
    colspan?: number
  }>(),
  {
    colspan: 1,
  },
)

const delegatedProps = reactiveOmit(props, 'class')
</script>

<template>
  <TableRow>
    <td data-slot="table-empty" :class="['fui-table-empty align-middle whitespace-nowrap', props.class]" v-bind="delegatedProps">
      <div class="flex items-center justify-center">
        <slot />
      </div>
    </td>
  </TableRow>
</template>
```

`components/ui/table/TableFooter.vue`

```vue
<script lang="ts">
/** Holds the summary rows at the bottom of the table, such as totals. */
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: 'tfoot' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="table-footer" :class="['fui-table-footer *:last:border-b-0', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/table/TableHead.vue`

```vue
<script lang="ts">
/** Labels one column of the table. */
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: 'th' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="table-head" :class="['fui-table-head align-middle whitespace-nowrap has-[[role=checkbox]]:pe-0', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/table/TableHeader.vue`

```vue
<script lang="ts">
/** Holds the row of column labels at the top of the table. */
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: 'thead' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="table-header" :class="props.class">
    <slot />
  </Primitive>
</template>
```

`components/ui/table/TableRow.vue`

```vue
<script lang="ts">
/** Groups the cells that belong to one entry of the table. */
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: 'tr' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="table-row" :data-selected="$attrs['data-state'] === 'selected' || undefined" :class="['fui-table-row transition-colors', props.class]">
    <slot />
  </Primitive>
</template>
```

## API of the items the example uses

### Badge

[Full docs](https://ui.frontic.com/components/ui/badge.md) · `npx @frontic/ui add badge`

#### Badge

Marks an item with a short label such as new or sale. Renders a `<span>`.

`data-slot="badge"` · `Badge.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` | `'span'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `variant` | `'neutral' \| 'info' \| 'success' \| 'warning' \| 'error' \| 'primary' \| 'promo' \| 'discount' \| 'new' \| 'price'` | `'neutral'` | What the badge marks: a state (info, success, warning, error), a commerce label (promo, discount, new, price), the brand colour (primary) or nothing in particular (neutral). Each takes its own colour and form. |
| `size` | `'md' \| 'sm' \| 'lg'` | `'md'` | The badge's padding and text size: sm keeps the smallest text, md and lg take the caption style. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-variant` | `'neutral' \| 'info' \| 'success' \| 'warning' \| 'error' \| 'primary' \| 'promo' \| 'discount' \| 'new' \| 'price'` | Carries the `variant` prop. |

### Price

[Full docs](https://ui.frontic.com/components/ui/price.md) · `npx @frontic/ui add price`

#### Price

Shows a price, with the original price and discount percentage when reduced. Renders a `<span>`.

`data-slot="price"` · `Price.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `size` | `PriceSize`: `'md' \| 'sm' \| 'lg'` | `'md'` | The text size of the price. |
| `align` | `PriceAlign`: `'start' \| 'end'` | `'start'` | Which side the badge, the prices and the hint line up on. |
| `price` (required) | `PriceType` |  | The price to show: `amount` and `ref` in minor units such as cents, `currency` as an ISO 4217 code. |
| `locale` | `string` |  | The locale that formats the price, e.g. `de-DE`; the ConfigProvider's when left out. |
| `showRef` | `boolean` | `true` | Shows the reference price struck through when it is higher than the amount. |
| `showDiscount` | `boolean` | `true` | Shows the saving as a percentage next to a discounted price. |
| `showHint` | `boolean` | `true` | Shows the hint below the price when the hint slot holds one. |
| `refLabel` | `string` | `'Was'` | Read out before the original price of a reduced product; hidden on screen. |
| `amountLabel` | `string` | `'Now'` | Read out before the current price of a reduced product; hidden on screen. |

| Slot | Slot props | Description |
| --- | --- | --- |
| `hint` |  | A note below the price, such as tax and shipping. |

#### PriceAmount

Shows the current price of the product. Renders a `<span>`.

`data-slot="price-amount"` · `PriceAmount.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

#### PriceRef

Shows the original price of a discounted product. Renders a `<span>`.

`data-slot="price-ref"` · `PriceRef.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

#### PriceDiscount

Shows the percentage saved against the original price. Renders a `<span>`.

`data-slot="price-discount"` · `PriceDiscount.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Slot | Slot props | Description |
| --- | --- | --- |
| `default` | `{ percent: number; }` | Replaces the discount badge; gets the percentage saved. |

#### PriceHint

Holds a note below the price, such as tax and shipping, which may link on. Renders a `<span>`.

`data-slot="price-hint"` · `PriceHint.vue`

Styling: `price-hint`, which extends [`link-foreground`](https://ui.frontic.com/DESIGN.md#link-foreground). 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` | `'span'` | 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. |
