- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Avatar
- Badge
- Breadcrumb
- Button
- Button Group
- Calendar
- Card
- Carousel
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Filter
- Form
- Hover Card
- Image
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- Mega Menu
- Menubar
- Native Select
- Navigation Menu
- Number Field
- Pagination
- Pin Input
- Popover
- Price
- Progress
- Radio Group
- Radio Stack
- Range Calendar
- Rating
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Stepper
- Swatch
- Swatch Group
- Switch
- Table
- Tabs
- Tags Input
- Textarea
- Toast
- Toggle
- Toggle Group
- Tooltip
- Typography
This is a Frontic UI custom component.
<script setup lang="ts">
import { Price } from '@/components/ui/price'
</script>
<template>
<div class="flex flex-wrap items-center gap-6">
<Price :price="{ amount: 12999, currency: 'EUR' }" />
<Price :price="{ amount: 4999, currency: 'USD' }" />
<Price :price="{ amount: 8999, currency: 'EUR', ref: 12999 }" />
</div>
</template>Installation
pnpm dlx @frontic/ui add price
Usage
<script setup lang="ts">
import { Price } from '@/components/ui/price'
</script>
<template>
<Price :price="{ amount: 12999, currency: 'EUR' }" />
</template>The Price component accepts the Frontic API Price type directly. Amounts are stored as integers in the smallest currency unit (e.g., cents) and formatted using Intl.NumberFormat with the browser's locale.
Custom Composition
Use the sub-components to build custom price layouts while keeping the formatting logic.
<script setup lang="ts">
import {
Price,
PriceAmount,
PriceRef,
PriceDiscount,
} from '@/components/ui/price'
</script>
<template>
<Price :price="product.price">
<PriceAmount class="text-2xl font-bold" />
<PriceRef />
<PriceDiscount v-slot="{ percent }">
<span class="text-sm text-green-600">Save {{ percent }}%</span>
</PriceDiscount>
</Price>
</template>When you provide a default slot, the Price wrapper still handles all formatting via provide/inject — the sub-components just read from it. If a sub-component is used outside a Price parent, it renders nothing.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
price | PriceType | (required) | Price object with amount, currency, precision, ref |
size | 'sm' | 'md' | 'lg' | 'md' | Font size variant |
locale | string | browser | BCP 47 locale tag for formatting |
showRef | boolean | true | Show strikethrough reference price when available |
showDiscount | boolean | true | Show discount percentage when ref price exists |
PriceType
interface PriceType {
amount: number // Price in smallest unit (e.g., 12999 = 129.99)
currency: string // ISO 4217 currency code (e.g., 'EUR', 'USD')
precision?: number // Decimal places (default: 2)
ref?: number // Reference/original price for sale display
}Sizes
Three size variants for different contexts.
<script setup lang="ts">
import { Price } from '@/components/ui/price'
</script>
<template>
<div class="flex flex-col gap-4">
<div class="flex items-center gap-4">
<span class="w-20 text-sm text-muted-foreground">Small</span>
<Price size="sm" :price="{ amount: 12999, currency: 'EUR' }" />
</div>
<div class="flex items-center gap-4">
<span class="w-20 text-sm text-muted-foreground">Medium</span>
<Price size="md" :price="{ amount: 12999, currency: 'EUR' }" />
</div>
<div class="flex items-center gap-4">
<span class="w-20 text-sm text-muted-foreground">Large</span>
<Price size="lg" :price="{ amount: 12999, currency: 'EUR' }" />
</div>
</div>
</template>Sale Price
When the ref field is higher than amount, the component automatically shows the reference price as strikethrough and calculates the discount percentage.
<script setup lang="ts">
import { Price } from '@/components/ui/price'
</script>
<template>
<div class="flex flex-col gap-4">
<div class="flex items-center gap-4">
<span class="w-20 text-sm text-muted-foreground">Small</span>
<Price size="sm" :price="{ amount: 8999, currency: 'EUR', ref: 12999 }" />
</div>
<div class="flex items-center gap-4">
<span class="w-20 text-sm text-muted-foreground">Medium</span>
<Price size="md" :price="{ amount: 8999, currency: 'EUR', ref: 12999 }" />
</div>
<div class="flex items-center gap-4">
<span class="w-20 text-sm text-muted-foreground">Large</span>
<Price size="lg" :price="{ amount: 8999, currency: 'EUR', ref: 12999 }" />
</div>
</div>
</template>Examples
Without Discount Badge
Show the sale price without the percentage badge.
<script setup lang="ts">
import { Price } from '@/components/ui/price'
</script>
<template>
<div class="flex flex-wrap items-center gap-6">
<Price :price="{ amount: 8999, currency: 'EUR', ref: 12999 }" :show-discount="false" />
<Price size="lg" :price="{ amount: 15900, currency: 'EUR', ref: 19900 }" :show-discount="false" />
</div>
</template>Different Currencies
The component formats prices based on the currency code and browser locale.
<script setup lang="ts">
import { Price } from '@/components/ui/price'
</script>
<template>
<div class="flex flex-col gap-4">
<div class="flex items-center gap-4">
<span class="w-12 text-sm text-muted-foreground">EUR</span>
<Price :price="{ amount: 12999, currency: 'EUR' }" />
</div>
<div class="flex items-center gap-4">
<span class="w-12 text-sm text-muted-foreground">USD</span>
<Price :price="{ amount: 12999, currency: 'USD' }" />
</div>
<div class="flex items-center gap-4">
<span class="w-12 text-sm text-muted-foreground">GBP</span>
<Price :price="{ amount: 12999, currency: 'GBP' }" />
</div>
</div>
</template>SSR
When locale is not set, Intl.NumberFormat uses the runtime default locale. On Node.js this is typically en-US, while the browser may differ — causing a hydration mismatch.
Pass the locale prop explicitly in SSR contexts:
<Price :price="product.price" locale="de-DE" />API Reference
Price
The root component that formats prices and provides context to sub-components via provide/inject.
| Prop | Type | Default |
|---|---|---|
price | PriceType | (required) |
size | 'sm' | 'md' | 'lg' | 'md' |
locale | string | runtime |
showRef | boolean | true |
showDiscount | boolean | true |
| Slot | Props | Description |
|---|---|---|
default | — | Override the default layout (PriceAmount + PriceRef + PriceDiscount) |
PriceAmount
Displays the formatted current price.
| Prop | Type | Default |
|---|---|---|
class | HTMLAttributes['class'] | — |
| Slot | Props | Description |
|---|---|---|
default | — | Override the formatted amount |
PriceRef
Displays the strikethrough reference/original price. Only renders when isDiscounted is true.
| Prop | Type | Default |
|---|---|---|
class | HTMLAttributes['class'] | — |
| Slot | Props | Description |
|---|---|---|
default | — | Override the formatted ref price |
PriceDiscount
Displays the discount percentage badge. Exposes percent via scoped slot.
| Prop | Type | Default |
|---|---|---|
class | HTMLAttributes['class'] | — |
| Slot | Props | Description |
|---|---|---|
default | { percent: number } | Override the discount badge content |