PricingTablePRO
Usage
The PricingTable component provides a responsive and customizable way to display pricing plans in a table format, automatically switching between a horizontal table layout on desktop for easy comparison and a vertical card layout on mobile for better readability.
- SoloMost popularFor indie hackers.$249billed annually/monthFeaturesNumber of developers1ProjectsGitHub repository accessUpdatesPatch & minorSupportCommunitySecuritySSOAudit logsCustom security review
- TeamFor growing teams.$499billed annually/monthFeaturesNumber of developers5ProjectsGitHub repository accessUpdatesAll updatesSupportPrioritySecuritySSOAudit logsCustom security review
- EnterpriseFor large organizations.CustomFeaturesNumber of developersUnlimitedProjectsGitHub repository accessUpdatesAll updatesSupport24/7SecuritySSOAudit logsCustom security review
Tiers
Use the tiers
prop as an array of objects to define your pricing plans. Each tier object supports the following properties:
id: string
- Unique identifier for the tier (required)title?: string
- Name of the pricing plandescription?: string
- Short description of the planprice?: string
- The current price of the plan (e.g., "$99", "€99", "Free")discount?: string
- The discounted price that will display theprice
with strikethrough (e.g., "$79", "€79")billingCycle?: string
- The unit price period that appears next to the price (e.g., "/month", "/seat/month")billingPeriod?: string
- Additional billing context that appears above the billing cycle (e.g., "billed monthly")badge?: string | BadgeProps
- Display a badge next to the title{ color: 'primary', variant: 'subtle' }
button?: ButtonProps
- Configure the CTA button{ size: 'lg', block: true }
highlight?: boolean
- Whether to visually emphasize this tier as the recommended option
- SoloMost popularFor indie hackers.$249billed annually/month
- TeamFor growing teams.$499billed annually/month
- EnterpriseFor large organizations.Custom
<script setup lang="ts">
const tiers = ref([
{
id: 'solo',
title: 'Solo',
description: 'For indie hackers.',
price: '$249',
billingCycle: '/month',
billingPeriod: 'billed annually',
badge: 'Most popular',
button: {
label: 'Buy now',
variant: 'subtle'
}
},
{
id: 'team',
title: 'Team',
description: 'For growing teams.',
price: '$499',
billingCycle: '/month',
billingPeriod: 'billed annually',
button: {
label: 'Buy now'
},
highlight: true
},
{
id: 'enterprise',
title: 'Enterprise',
description: 'For large organizations.',
price: 'Custom',
button: {
label: 'Contact sales',
color: 'neutral'
}
}
])
</script>
<template>
<UPricingTable :tiers="tiers" />
</template>
Sections
Use the sections
prop to organize features into logical groups. Each section represents a category of features that you want to compare across different pricing tiers.
title: string
- The heading for the feature sectionfeatures: PricingTableSectionFeature[]
- An array of features with their availability in each tier:- Each feature requires a
title
and atiers
object mapping tier IDs to values - Boolean values (
true
/false
) will display as checkmarks (✓) or minus icons (-) - String values will be shown as text (e.g., "Unlimited", "Up to 5 users")
- Numeric values will be displayed as is (e.g., 10, 100)
- Each feature requires a
- SoloFor indie hackers.$249Â /monthFeaturesNumber of developers1ProjectsSecuritySSO
- TeamFor growing teams.$499Â /monthFeaturesNumber of developers5ProjectsSecuritySSO
- EnterpriseFor large organizations.CustomFeaturesNumber of developersUnlimitedProjectsSecuritySSO
<script setup lang="ts">
const tiers = ref([
{
id: 'solo',
title: 'Solo',
price: '$249',
description: 'For indie hackers.',
billingCycle: '/month',
button: {
label: 'Buy now',
variant: 'subtle'
}
},
{
id: 'team',
title: 'Team',
price: '$499',
description: 'For growing teams.',
billingCycle: '/month',
button: {
label: 'Buy now'
}
},
{
id: 'enterprise',
title: 'Enterprise',
price: 'Custom',
description: 'For large organizations.',
button: {
label: 'Contact sales',
color: 'neutral'
}
}
])
const sections = ref([
{
title: 'Features',
features: [
{
title: 'Number of developers',
tiers: {
solo: '1',
team: '5',
enterprise: 'Unlimited'
}
},
{
title: 'Projects',
tiers: {
solo: true,
team: true,
enterprise: true
}
}
]
},
{
title: 'Security',
features: [
{
title: 'SSO',
tiers: {
solo: false,
team: true,
enterprise: true
}
}
]
}
])
</script>
<template>
<UPricingTable :tiers="tiers" :sections="sections" />
</template>
API
Props
Prop | Default | Type |
---|---|---|
as |
|
The element or component this component should render as. |
tiers |
The pricing tiers to display in the table. Each tier represents a pricing plan with its own title, description, price, and features.
| |
sections |
The sections of features to display in the table. Each section contains a title and a list of features with their availability in each tier. | |
caption |
The caption to display above the table. | |
ui |
|
Slots
Slot | Type |
---|---|
caption |
|
tier |
|
tier-title |
|
tier-description |
|
tier-badge |
|
tier-button |
|
tier-billing |
|
tier-discount |
|
tier-price |
|
Theme
export default defineAppConfig({
uiPro: {
pricingTable: {
slots: {
root: 'w-full relative',
table: 'w-full table-fixed border-separate border-spacing-x-0 hidden md:table',
list: 'md:hidden flex flex-col gap-6 w-full',
item: 'p-6 flex flex-col border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)]',
caption: 'sr-only',
thead: '',
tbody: '',
tr: '',
th: 'py-4 font-normal text-left border-b border-(--ui-border)',
td: 'px-6 py-4 text-center border-b border-(--ui-border)',
tier: 'p-6 text-left font-normal',
tierTitleWrapper: 'flex items-center gap-3',
tierTitle: 'text-lg font-semibold text-(--ui-text-highlighted)',
tierDescription: 'text-sm font-normal text-(--ui-text-muted) mt-1',
tierBadge: 'truncate',
tierPriceWrapper: 'flex items-center gap-1 mt-4',
tierPrice: 'text-(--ui-text-highlighted) text-3xl sm:text-4xl font-semibold',
tierDiscount: 'text-(--ui-text-muted) line-through text-xl sm:text-2xl',
tierBilling: 'flex flex-col justify-between min-w-0',
tierBillingPeriod: 'text-(--ui-text-toned) truncate text-xs font-medium',
tierBillingCycle: 'text-(--ui-text-muted) truncate text-xs font-medium',
tierButton: 'mt-6',
tierFeatureIcon: 'size-5 shrink-0',
section: 'mt-6 flex flex-col gap-2',
sectionTitle: 'font-semibold text-sm text-(--ui-text-highlighted)',
feature: 'flex items-center justify-between gap-1',
featureTitle: 'text-sm text-(--ui-text)',
featureValue: 'text-sm text-(--ui-text-muted) flex justify-center min-w-5'
},
variants: {
section: {
true: {
tr: '*:pt-8'
}
},
active: {
true: {
tierFeatureIcon: 'text-(--ui-primary)'
}
},
highlight: {
true: {
tier: 'bg-(--ui-bg-elevated)/50 border-x border-t border-(--ui-border) rounded-t-[calc(var(--ui-radius)*2)]',
td: 'bg-(--ui-bg-elevated)/50 border-x border-(--ui-border)',
item: 'bg-(--ui-bg-elevated)/50'
}
}
}
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
uiPro: {
pricingTable: {
slots: {
root: 'w-full relative',
table: 'w-full table-fixed border-separate border-spacing-x-0 hidden md:table',
list: 'md:hidden flex flex-col gap-6 w-full',
item: 'p-6 flex flex-col border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)]',
caption: 'sr-only',
thead: '',
tbody: '',
tr: '',
th: 'py-4 font-normal text-left border-b border-(--ui-border)',
td: 'px-6 py-4 text-center border-b border-(--ui-border)',
tier: 'p-6 text-left font-normal',
tierTitleWrapper: 'flex items-center gap-3',
tierTitle: 'text-lg font-semibold text-(--ui-text-highlighted)',
tierDescription: 'text-sm font-normal text-(--ui-text-muted) mt-1',
tierBadge: 'truncate',
tierPriceWrapper: 'flex items-center gap-1 mt-4',
tierPrice: 'text-(--ui-text-highlighted) text-3xl sm:text-4xl font-semibold',
tierDiscount: 'text-(--ui-text-muted) line-through text-xl sm:text-2xl',
tierBilling: 'flex flex-col justify-between min-w-0',
tierBillingPeriod: 'text-(--ui-text-toned) truncate text-xs font-medium',
tierBillingCycle: 'text-(--ui-text-muted) truncate text-xs font-medium',
tierButton: 'mt-6',
tierFeatureIcon: 'size-5 shrink-0',
section: 'mt-6 flex flex-col gap-2',
sectionTitle: 'font-semibold text-sm text-(--ui-text-highlighted)',
feature: 'flex items-center justify-between gap-1',
featureTitle: 'text-sm text-(--ui-text)',
featureValue: 'text-sm text-(--ui-text-muted) flex justify-center min-w-5'
},
variants: {
section: {
true: {
tr: '*:pt-8'
}
},
active: {
true: {
tierFeatureIcon: 'text-(--ui-primary)'
}
},
highlight: {
true: {
tier: 'bg-(--ui-bg-elevated)/50 border-x border-t border-(--ui-border) rounded-t-[calc(var(--ui-radius)*2)]',
td: 'bg-(--ui-bg-elevated)/50 border-x border-(--ui-border)',
item: 'bg-(--ui-bg-elevated)/50'
}
}
}
}
}
})
]
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'
export default defineConfig({
plugins: [
vue(),
uiPro({
uiPro: {
pricingTable: {
slots: {
root: 'w-full relative',
table: 'w-full table-fixed border-separate border-spacing-x-0 hidden md:table',
list: 'md:hidden flex flex-col gap-6 w-full',
item: 'p-6 flex flex-col border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)]',
caption: 'sr-only',
thead: '',
tbody: '',
tr: '',
th: 'py-4 font-normal text-left border-b border-(--ui-border)',
td: 'px-6 py-4 text-center border-b border-(--ui-border)',
tier: 'p-6 text-left font-normal',
tierTitleWrapper: 'flex items-center gap-3',
tierTitle: 'text-lg font-semibold text-(--ui-text-highlighted)',
tierDescription: 'text-sm font-normal text-(--ui-text-muted) mt-1',
tierBadge: 'truncate',
tierPriceWrapper: 'flex items-center gap-1 mt-4',
tierPrice: 'text-(--ui-text-highlighted) text-3xl sm:text-4xl font-semibold',
tierDiscount: 'text-(--ui-text-muted) line-through text-xl sm:text-2xl',
tierBilling: 'flex flex-col justify-between min-w-0',
tierBillingPeriod: 'text-(--ui-text-toned) truncate text-xs font-medium',
tierBillingCycle: 'text-(--ui-text-muted) truncate text-xs font-medium',
tierButton: 'mt-6',
tierFeatureIcon: 'size-5 shrink-0',
section: 'mt-6 flex flex-col gap-2',
sectionTitle: 'font-semibold text-sm text-(--ui-text-highlighted)',
feature: 'flex items-center justify-between gap-1',
featureTitle: 'text-sm text-(--ui-text)',
featureValue: 'text-sm text-(--ui-text-muted) flex justify-center min-w-5'
},
variants: {
section: {
true: {
tr: '*:pt-8'
}
},
active: {
true: {
tierFeatureIcon: 'text-(--ui-primary)'
}
},
highlight: {
true: {
tier: 'bg-(--ui-bg-elevated)/50 border-x border-t border-(--ui-border) rounded-t-[calc(var(--ui-radius)*2)]',
td: 'bg-(--ui-bg-elevated)/50 border-x border-(--ui-border)',
item: 'bg-(--ui-bg-elevated)/50'
}
}
}
}
}
})
]
})