PricingPlan
Usage
The PricingPlan component provides a flexible way to display a pricing plan with customizable content including title, description, price, features, etc.
- One developer
- Unlimited projects
- Access to GitHub repository
- Unlimited patch & minor updates
- Lifetime access
Title
Use the title
prop to set the title of the PricingPlan.
<template>
<UPricingPlan title="Solo" class="w-96" />
</template>
Description
Use the description
prop to set the description of the PricingPlan.
<template>
<UPricingPlan title="Solo" description="For bootstrappers and indie hackers." />
</template>
Badge
Use the badge
prop to display a Badge next to the title of the PricingPlan.
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
badge="Most popular"
/>
</template>
You can pass any property from the Badge component to customize it.
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
:badge="{
label: 'Most popular',
color: 'neutral',
variant: 'solid'
}"
/>
</template>
Price
Use the price
prop to set the price of the PricingPlan.
<template>
<UPricingPlan title="Solo" description="For bootstrappers and indie hackers." price="$249" />
</template>
Discount
Use the discount
prop to set a discounted price that will be displayed alongside the original price (which will be shown with a strikethrough).
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
price="$249"
discount="$199"
/>
</template>
Billing
Use the billing-cycle
and/or billing-period
props to display the billing information of the PricingPlan.
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
price="$9"
billing-cycle="/month"
billing-period="billed annually"
/>
</template>
Features
Use the features
prop as an array of string to display a list of features on the PricingPlan:
- One developer
- Unlimited projects
- Access to GitHub repository
- Unlimited patch & minor updates
- Lifetime access
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
price="$249"
:features="[
'One developer',
'Unlimited projects',
'Access to GitHub repository',
'Unlimited patch & minor updates',
'Lifetime access'
]"
/>
</template>
You can also pass an array of objects with the following properties:
title: string
icon?: string
- One developer
- Unlimited projects
- Access to GitHub repository
- Unlimited patch & minor updates
- Lifetime access
<script setup lang="ts">
const features = ref([
{
title: 'One developer',
icon: 'i-lucide-user'
},
{
title: 'Unlimited projects',
icon: 'i-lucide-infinity'
},
{
title: 'Access to GitHub repository',
icon: 'i-lucide-github'
},
{
title: 'Unlimited patch & minor updates',
icon: 'i-lucide-refresh-cw'
},
{
title: 'Lifetime access',
icon: 'i-lucide-clock'
}
])
</script>
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
price="$249"
:features="features"
/>
</template>
Button
Use the button
prop with any property from the Button component to display a button at the bottom of the PricingPlan.
- One developer
- Unlimited projects
- Access to GitHub repository
- Unlimited patch & minor updates
- Lifetime access
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
price="$249"
:features="[
'One developer',
'Unlimited projects',
'Access to GitHub repository',
'Unlimited patch & minor updates',
'Lifetime access'
]"
:button="{
label: 'Buy now'
}"
/>
</template>
onClick
field to add a click handler to trigger the plan purchase.Variant
Use the variant
prop to change the variant of the PricingPlan.
- One developer
- Unlimited projects
- Access to GitHub repository
- Unlimited patch & minor updates
- Lifetime access
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
price="$249"
:features="[
'One developer',
'Unlimited projects',
'Access to GitHub repository',
'Unlimited patch & minor updates',
'Lifetime access'
]"
:button="{
label: 'Buy now'
}"
variant="subtle"
/>
</template>
Orientation
Use the orientation
prop to change the orientation of the PricingPlan. Defaults to vertical
.
- One developer
- Unlimited projects
- Access to GitHub repository
- Lifetime access
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
price="$249"
:features="[
'One developer',
'Unlimited projects',
'Access to GitHub repository',
'Lifetime access'
]"
:button="{
label: 'Buy now'
}"
orientation="horizontal"
/>
</template>
Tagline
Use the tagline
prop to display a tagline text above the price.
- One developer
- Unlimited projects
- Access to GitHub repository
- Lifetime access
Pay once, own it forever
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
price="$249"
:features="[
'One developer',
'Unlimited projects',
'Access to GitHub repository',
'Lifetime access'
]"
:button="{
label: 'Buy now'
}"
orientation="horizontal"
tagline="Pay once, own it forever"
/>
</template>
Terms
Use the terms
prop to display terms below the price.
- One developer
- Unlimited projects
- Access to GitHub repository
- Lifetime access
Pay once, own it forever
Invoices and receipts available.
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
price="$249"
:features="[
'One developer',
'Unlimited projects',
'Access to GitHub repository',
'Lifetime access'
]"
:button="{
label: 'Buy now'
}"
orientation="horizontal"
tagline="Pay once, own it forever"
terms="Invoices and receipts available."
/>
</template>
Highlight
Use the highlight
prop to display a highlighted border around the PricingPlan.
- One developer
- Unlimited projects
- Access to GitHub repository
- Unlimited patch & minor updates
- Lifetime access
<template>
<UPricingPlan
title="Solo"
description="For bootstrappers and indie hackers."
price="$249"
:features="[
'One developer',
'Unlimited projects',
'Access to GitHub repository',
'Unlimited patch & minor updates',
'Lifetime access'
]"
:button="{
label: 'Buy now'
}"
highlight
/>
</template>
Scale
Use the scale
prop to make a PricingPlan bigger than the others.
scale
example to see how it works as it's hard to demonstrate by itself.API
Props
Prop | Default | Type |
---|---|---|
as |
|
The element or component this component should render as. |
title |
| |
description |
| |
badge |
Display a badge on the pricing plan next to the title.
Can be a string or an object.
| |
billingCycle |
The unit price period that appears next to the price. Typically used to show the recurring interval. | |
billingPeriod |
Additional billing context that appears above the billing cycle. Typically used to show the actual billing frequency. | |
price |
The current price of the plan.
When used with | |
discount |
The discounted price of the plan.
When provided, the | |
features |
Display a list of features under the price. Can be an array of strings or an array of objects. | |
button |
Display a buy button at the bottom of the pricing plan.
| |
tagline |
Display a tagline highlighting the pricing value proposition. | |
terms |
Display terms at the bottom of the pricing plan. | |
orientation |
|
The orientation of the pricing plan. |
variant |
|
|
highlight |
Display a ring around the pricing plan to highlight it. | |
scale |
Enlarge the plan to make it more prominent. | |
ui |
|
Slots
Slot | Type |
---|---|
badge |
|
title |
|
description |
|
price |
|
discount |
|
billing |
|
features |
|
button |
|
header |
|
body |
|
footer |
|
Theme
export default defineAppConfig({
uiPro: {
pricingPlan: {
slots: {
root: 'relative grid rounded-[calc(var(--ui-radius)*2.5)] p-6 lg:p-8 xl:p-10 gap-6',
header: '',
body: 'flex flex-col min-w-0',
footer: 'flex flex-col gap-6 items-center',
titleWrapper: 'flex items-center gap-3',
title: 'text-[var(--ui-text-highlighted)] text-2xl sm:text-3xl text-pretty font-semibold',
description: 'text-base text-pretty mt-2',
priceWrapper: 'flex items-center gap-1',
price: 'text-[var(--ui-text-highlighted)] text-3xl sm:text-4xl font-semibold',
discount: 'text-[var(--ui-text-muted)] line-through text-xl sm:text-2xl',
billing: 'flex flex-col justify-between min-w-0',
billingPeriod: 'text-[var(--ui-text-toned)] truncate text-xs font-medium',
billingCycle: 'text-[var(--ui-text-muted)] truncate text-xs font-medium',
features: 'flex flex-col gap-3 flex-1 mt-6 grow-0',
feature: 'flex items-center gap-2 min-w-0',
featureIcon: 'size-5 shrink-0 text-[var(--ui-primary)]',
featureTitle: 'text-sm truncate',
badge: '',
button: '',
tagline: 'text-base font-semibold text-[var(--ui-text)]',
terms: 'text-xs/5 text-[var(--ui-text-muted)] text-center text-balance'
},
variants: {
orientation: {
horizontal: {
root: 'grid-cols-1 lg:grid-cols-3 justify-between divide-y lg:divide-y-0 lg:divide-x divide-[var(--ui-border)]',
body: 'lg:col-span-2 pb-6 lg:pb-0 lg:pr-6 justify-center',
footer: 'lg:justify-center lg:items-center lg:p-6 lg:max-w-xs lg:w-full lg:mx-auto',
features: 'lg:grid lg:grid-cols-2 lg:mt-12'
},
vertical: {
footer: 'justify-end',
priceWrapper: 'mt-6'
}
},
variant: {
solid: {
root: 'bg-[var(--ui-bg-inverted)]',
title: 'text-[var(--ui-bg)]',
description: 'text-[var(--ui-text-dimmed)]',
price: 'text-[var(--ui-bg)]',
discount: 'text-[var(--ui-text-dimmed)]',
billingCycle: 'text-[var(--ui-text-dimmed)]',
billingPeriod: 'text-[var(--ui-text-dimmed)]',
featureTitle: 'text-[var(--ui-text-dimmed)]'
},
outline: {
root: 'bg-[var(--ui-bg)] ring ring-inset ring-[var(--ui-border)]',
description: 'text-[var(--ui-text-muted)]',
featureTitle: 'text-[var(--ui-text-muted)]'
},
soft: {
root: 'bg-[var(--ui-bg-elevated)]/50',
description: 'text-[var(--ui-text-toned)]',
featureTitle: 'text-[var(--ui-text-toned)]'
},
subtle: {
root: 'bg-[var(--ui-bg-elevated)] ring ring-inset ring-[var(--ui-border-accented)]',
description: 'text-[var(--ui-text)]',
featureTitle: 'text-[var(--ui-text)]'
}
},
highlight: {
true: {
root: 'ring-2 ring-inset ring-[var(--ui-primary)]'
}
},
scale: {
true: {
root: 'lg:scale-[1.1] lg:z-[1]'
}
}
},
compoundVariants: [
{
orientation: 'horizontal',
variant: 'soft',
class: {
root: 'divide-[var(--ui-border-accented)]'
}
},
{
orientation: 'horizontal',
variant: 'subtle',
class: {
root: 'divide-[var(--ui-border-accented)]'
}
}
],
defaultVariants: {
color: 'primary',
variant: 'outline'
}
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
uiPro: {
pricingPlan: {
slots: {
root: 'relative grid rounded-[calc(var(--ui-radius)*2.5)] p-6 lg:p-8 xl:p-10 gap-6',
header: '',
body: 'flex flex-col min-w-0',
footer: 'flex flex-col gap-6 items-center',
titleWrapper: 'flex items-center gap-3',
title: 'text-[var(--ui-text-highlighted)] text-2xl sm:text-3xl text-pretty font-semibold',
description: 'text-base text-pretty mt-2',
priceWrapper: 'flex items-center gap-1',
price: 'text-[var(--ui-text-highlighted)] text-3xl sm:text-4xl font-semibold',
discount: 'text-[var(--ui-text-muted)] line-through text-xl sm:text-2xl',
billing: 'flex flex-col justify-between min-w-0',
billingPeriod: 'text-[var(--ui-text-toned)] truncate text-xs font-medium',
billingCycle: 'text-[var(--ui-text-muted)] truncate text-xs font-medium',
features: 'flex flex-col gap-3 flex-1 mt-6 grow-0',
feature: 'flex items-center gap-2 min-w-0',
featureIcon: 'size-5 shrink-0 text-[var(--ui-primary)]',
featureTitle: 'text-sm truncate',
badge: '',
button: '',
tagline: 'text-base font-semibold text-[var(--ui-text)]',
terms: 'text-xs/5 text-[var(--ui-text-muted)] text-center text-balance'
},
variants: {
orientation: {
horizontal: {
root: 'grid-cols-1 lg:grid-cols-3 justify-between divide-y lg:divide-y-0 lg:divide-x divide-[var(--ui-border)]',
body: 'lg:col-span-2 pb-6 lg:pb-0 lg:pr-6 justify-center',
footer: 'lg:justify-center lg:items-center lg:p-6 lg:max-w-xs lg:w-full lg:mx-auto',
features: 'lg:grid lg:grid-cols-2 lg:mt-12'
},
vertical: {
footer: 'justify-end',
priceWrapper: 'mt-6'
}
},
variant: {
solid: {
root: 'bg-[var(--ui-bg-inverted)]',
title: 'text-[var(--ui-bg)]',
description: 'text-[var(--ui-text-dimmed)]',
price: 'text-[var(--ui-bg)]',
discount: 'text-[var(--ui-text-dimmed)]',
billingCycle: 'text-[var(--ui-text-dimmed)]',
billingPeriod: 'text-[var(--ui-text-dimmed)]',
featureTitle: 'text-[var(--ui-text-dimmed)]'
},
outline: {
root: 'bg-[var(--ui-bg)] ring ring-inset ring-[var(--ui-border)]',
description: 'text-[var(--ui-text-muted)]',
featureTitle: 'text-[var(--ui-text-muted)]'
},
soft: {
root: 'bg-[var(--ui-bg-elevated)]/50',
description: 'text-[var(--ui-text-toned)]',
featureTitle: 'text-[var(--ui-text-toned)]'
},
subtle: {
root: 'bg-[var(--ui-bg-elevated)] ring ring-inset ring-[var(--ui-border-accented)]',
description: 'text-[var(--ui-text)]',
featureTitle: 'text-[var(--ui-text)]'
}
},
highlight: {
true: {
root: 'ring-2 ring-inset ring-[var(--ui-primary)]'
}
},
scale: {
true: {
root: 'lg:scale-[1.1] lg:z-[1]'
}
}
},
compoundVariants: [
{
orientation: 'horizontal',
variant: 'soft',
class: {
root: 'divide-[var(--ui-border-accented)]'
}
},
{
orientation: 'horizontal',
variant: 'subtle',
class: {
root: 'divide-[var(--ui-border-accented)]'
}
}
],
defaultVariants: {
color: 'primary',
variant: 'outline'
}
}
}
})
]
})