10.1k

Price

PreviousNext

Displays formatted prices with locale-aware currency formatting, sale/reference prices, and discount percentages.

€129.99$49.99€89.99€129.99
-31%
<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

PropTypeDefaultDescription
pricePriceType(required)Price object with amount, currency, precision, ref
size'sm' | 'md' | 'lg''md'Font size variant
localestringbrowserBCP 47 locale tag for formatting
showRefbooleantrueShow strikethrough reference price when available
showDiscountbooleantrueShow 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.

Small€129.99
Medium€129.99
Large€129.99
<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.

Small€89.99€129.99
-31%
Medium€89.99€129.99
-31%
Large€89.99€129.99
-31%
<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.

€89.99€129.99€159.00€199.00
<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.

EUR€129.99
USD$129.99
GBP£129.99
<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.

PropTypeDefault
pricePriceType(required)
size'sm' | 'md' | 'lg''md'
localestringruntime
showRefbooleantrue
showDiscountbooleantrue
SlotPropsDescription
defaultOverride the default layout (PriceAmount + PriceRef + PriceDiscount)

PriceAmount

Displays the formatted current price.

PropTypeDefault
classHTMLAttributes['class']
SlotPropsDescription
defaultOverride the formatted amount

PriceRef

Displays the strikethrough reference/original price. Only renders when isDiscounted is true.

PropTypeDefault
classHTMLAttributes['class']
SlotPropsDescription
defaultOverride the formatted ref price

PriceDiscount

Displays the discount percentage badge. Exposes percent via scoped slot.

PropTypeDefault
classHTMLAttributes['class']
SlotPropsDescription
default{ percent: number }Override the discount badge content