PricingTable

機能比較とともに階層的な価格プランを表示するレスポンシブな価格表コンポーネント。

使用法

PricingTableコンポーネントは、テーブル形式で価格プランを表示するための応答性とカスタマイズ性の高い方法を提供します。比較しやすいデスクトップ上の水平のテーブルレイアウトと、読みやすいモバイル上の垂直のカードライアウトを自動的に切り替えます。

  • Solo
    Most popular
    For indie hackers.
    $249
    billed annually/month
    Features
    Number of developers
    1
    Projects
    GitHub repository access
    Updates
    Patch & minor
    Support
    Community
    Security
    SSO
    Audit logs
    Custom security review
  • Team
    For growing teams.
    $499
    billed annually/month
    Features
    Number of developers
    5
    Projects
    GitHub repository access
    Updates
    All updates
    Support
    Priority
    Security
    SSO
    Audit logs
    Custom security review
  • Enterprise
    For large organizations.
    Custom
    Features
    Number of developers
    Unlimited
    Projects
    GitHub repository access
    Updates
    All updates
    Support
    24/7
    Security
    SSO
    Audit logs
    Custom security review

Tier

tiersプロパティをオブジェクトの配列として使用して、料金プランを定義します。各階層オブジェクトは以下のプロパティをサポートします。

  • id: string—階層の一意の識別子(必須)
  • title?: string—料金プラン名
  • description?: string—プランの簡単な説明
  • price?: string—プランの現在の価格(例:"$99"、"€ 99"、"Free")
  • discount?: string—priceがストライクスルー付きで表示される割引価格例"$79""€ 79"
  • billingCycle?: string—価格の横に表示される単価期間(例:"/month"、"/seat/month")
  • billingPeriod?: string—請求サイクルの上に表示される追加の請求コンテキスト(例:“毎月請求”)。
  • badge?: string | BadgeProps—タイトル{ color: 'primary', variant: 'subtle' }の横にバッジを表示する
  • button?: ButtonProps—CTAボタンの設定{ size: 'lg', block: true }
  • highlight?: boolean—推奨オプションとしてこの階層を視覚的に強調するかどうか
  • Solo
    Most popular
    For indie hackers.
    $249
    billed annually/month
  • Team
    For growing teams.
    $499
    billed annually/month
  • Enterprise
    For large organizations.
    Custom
<script setup lang="ts">
import type { PricingTableTier } from '@nuxt/ui'

const tiers = ref<PricingTableTier[]>([
  {
    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>
<script setup lang="ts">
import { ref } from 'vue'
import type { PricingTableTier } from '@nuxt/ui'

const tiers = ref<PricingTableTier[]>([
  {
    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プロパティを使用して、機能を論理グループに編成します。各セクションは、異なる価格帯で比較したい機能のカテゴリを表します。

  • title: string—フィーチャーセクションの見出し
  • features: PricingTableSectionFeature[]—各階層で利用可能な機能の配列:
    • 各機能には、階層IDを値にマッピングするtitleとtiersオブジェクトが必要です。
    • Boolean値true/falseはチェックマーク✓またはマイナスアイコン—として表示されます。
    • Stringの値はテキストとして表示されます例"Unlimited""Max to 5 users"
    • 数値はそのまま表示されます(例:10、100)
  • Solo
    For indie hackers.
    $249
     /month
    Features
    Number of developers
    1
    Projects
    Security
    SSO
  • Team
    For growing teams.
    $499
     /month
    Features
    Number of developers
    5
    Projects
    Security
    SSO
  • Enterprise
    For large organizations.
    Custom
    Features
    Number of developers
    Unlimited
    Projects
    Security
    SSO
<script setup lang="ts">
import type { PricingTableTier, PricingTableSection } from '@nuxt/ui'

const tiers = ref<PricingTableTier[]>([
  {
    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<PricingTableSection[]>([
  {
    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>
<script setup lang="ts">
import { ref } from 'vue'
import type { PricingTableTier, PricingTableSection } from '@nuxt/ui'

const tiers = ref<PricingTableTier[]>([
  {
    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<PricingTableSection[]>([
  {
    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>

サンプル

スロット付き

PricingTableコンポーネントは、コンテンツの表示を調整するための強力なスロットカスタマイズオプションを提供します。汎用スロットを使用して個々の要素をカスタマイズしたり、IDを使用して特定のアイテムをターゲットにしたりできます。

  • Solo
    For indie hackers.
    $249
     /month
    Features
    Number of developers
    1
    Projects
    Security
    SSO
  • Team
    For growing teams.
    $499
     /month
    Features
    Number of developers
    5
    Projects
    Security
    SSO
  • Enterprise
    For large organizations.
    Custom
    Features
    Number of developers
    Unlimited
    Projects
    Security
    SSO
<script setup lang="ts">
const tiers = [
  {
    id: 'solo',
    title: 'Solo',
    price: '$249',
    description: 'For indie hackers.',
    billingCycle: '/month',
    button: { label: 'Buy now', variant: 'subtle' as const }
  },
  {
    id: 'team',
    title: 'Team',
    price: '$499',
    description: 'For growing teams.',
    billingCycle: '/month',
    button: { label: 'Buy now' },
    highlight: true
  },
  {
    id: 'enterprise',
    title: 'Enterprise',
    price: 'Custom',
    description: 'For large organizations.',
    button: { label: 'Contact sales', color: 'neutral' as const }
  }
]

const sections = [
  {
    id: 'features',
    title: 'Features',
    features: [
      {
        id: 'developers',
        title: 'Number of developers',
        tiers: { solo: '1', team: '5', enterprise: 'Unlimited' }
      },
      {
        id: 'projects',
        title: 'Projects',
        tiers: { solo: true, team: true, enterprise: true }
      }
    ]
  },
  {
    id: 'security',
    title: 'Security',
    features: [
      {
        title: 'SSO',
        tiers: { solo: false, team: true, enterprise: true }
      }
    ]
  }
]
</script>

<template>
  <UPricingTable :tiers="tiers" :sections="sections">
    <!-- Customize specific tier title -->
    <template #team-title="{ tier }">
      <div class="flex items-center gap-2">
        <UIcon name="i-lucide-crown" class="size-4 text-amber-500" />
        {{ tier.title }}
      </div>
    </template>

    <!-- Customize specific section title -->
    <template #section-security-title="{ section }">
      <div class="flex items-center gap-2">
        <UIcon name="i-lucide-shield-check" class="size-4 text-green-500" />
        <span class="font-semibold text-green-700">{{ section.title }}</span>
      </div>
    </template>

    <!-- Customize specific feature value -->
    <template #feature-developers-value="{ feature, tier }">
      <template v-if="feature.tiers?.[tier.id]">
        <UBadge :label="String(feature.tiers[tier.id])" color="primary" variant="soft" />
      </template>
      <UIcon v-else name="i-lucide-x" class="size-4 text-muted" />
    </template>
  </UPricingTable>
</template>

カスタマイズの柔軟性を最大限に高めるため、さまざまなスロットタイプをサポート。

スロットタイプパターン説明例
Tierスロット#{tier-id}-{element}ターゲット特定階層#team-title、#solo-price
セクションスロット#section-{id|formatted-title}-title対象セクション#section-features-title
フィーチャースロット#feature-{id|formatted-title}-{title|value}ターゲット固有の機能#feature-developers-title
汎用スロット#tier-title、#section-titleなどすべての項目に適用#feature-value
idが指定されていない場合、スロット名はタイトルから自動生成されます(例:“Premium Features!”は#section-premium-features-titleになります)。

API

Props

Prop Default Type
as
'div'
any

The element or component this component should render as.

tiers
T[]

The pricing tiers to display in the table. Each tier represents a pricing plan with its own title, description, price, and features.

sections
PricingTableSection<T>[]

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
t('pricingTable.caption')
string

The caption to display above the table.

ui
{ root?: SlotClass; table?: SlotClass; list?: SlotClass; item?: SlotClass; caption?: SlotClass; thead?: SlotClass; tbody?: SlotClass; tr?: SlotClass; th?: SlotClass; td?: SlotClass; tier?: SlotClass; tierWrapper?: SlotClass; tierTitleWrapper?: SlotClass; tierTitle?: SlotClass; tierDescription?: SlotClass; tierBadge?: SlotClass; tierPriceWrapper?: SlotClass; tierPrice?: SlotClass; tierDiscount?: SlotClass; tierBilling?: SlotClass; tierBillingPeriod?: SlotClass; tierBillingCycle?: SlotClass; tierButton?: SlotClass; tierFeatureIcon?: SlotClass; section?: SlotClass; sectionTitle?: SlotClass; feature?: SlotClass; featureTitle?: SlotClass; featureValue?: SlotClass; }

スロット

Slot Type
caption
{}
tier
{ tier: T; }
tier-title
{ tier: T; }
tier-description
{ tier: T; }
tier-badge
{ tier: T; }
tier-button
{ tier: T; }
tier-billing
{ tier: T; }
tier-discount
{ tier: T; }
tier-price
{ tier: T; }
section-title
{ section: PricingTableSection<T>; }
feature-title
{ feature: PricingTableSectionFeature<T>; section: PricingTableSection<T>; }
feature-value
{ feature: PricingTableSectionFeature<T>; tier: T; section: PricingTableSection<T>; }

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    pricingTable: {
      slots: {
        root: 'w-full relative',
        table: 'w-full table-fixed border-separate border-spacing-x-0 hidden md:table h-fit',
        list: 'md:hidden flex flex-col gap-6 w-full',
        item: 'p-6 flex flex-col border border-default rounded-lg',
        caption: 'sr-only',
        thead: '',
        tbody: '',
        tr: '',
        th: 'py-4 font-normal text-start border-b border-default',
        td: 'px-6 py-4 text-center border-b border-default',
        tier: 'p-6 text-start font-normal align-top h-full',
        tierWrapper: 'flex flex-col md:h-full',
        tierTitleWrapper: 'flex items-center gap-3',
        tierTitle: 'text-lg font-semibold text-highlighted',
        tierDescription: 'text-sm font-normal text-muted mt-1',
        tierBadge: 'truncate',
        tierPriceWrapper: 'flex items-center gap-1 mt-4',
        tierPrice: 'text-highlighted text-3xl sm:text-4xl font-semibold',
        tierDiscount: 'text-muted line-through text-xl sm:text-2xl',
        tierBilling: 'flex flex-col justify-between min-w-0',
        tierBillingPeriod: 'text-toned truncate text-xs font-medium',
        tierBillingCycle: 'text-muted truncate text-xs font-medium',
        tierButton: 'mt-6 md:mt-auto md:pt-6',
        tierFeatureIcon: 'size-5 shrink-0',
        section: 'mt-6 flex flex-col gap-2',
        sectionTitle: 'font-semibold text-sm text-highlighted',
        feature: 'flex items-center justify-between gap-1',
        featureTitle: 'text-sm text-default',
        featureValue: 'text-sm text-muted flex justify-center min-w-5'
      },
      variants: {
        section: {
          true: {
            tr: '*:pt-8'
          }
        },
        active: {
          true: {
            tierFeatureIcon: 'text-primary'
          }
        },
        highlight: {
          true: {
            tier: 'bg-elevated/50 border-x border-t border-default rounded-t-lg',
            td: 'bg-elevated/50 border-x border-default',
            item: 'bg-elevated/50'
          }
        }
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        pricingTable: {
          slots: {
            root: 'w-full relative',
            table: 'w-full table-fixed border-separate border-spacing-x-0 hidden md:table h-fit',
            list: 'md:hidden flex flex-col gap-6 w-full',
            item: 'p-6 flex flex-col border border-default rounded-lg',
            caption: 'sr-only',
            thead: '',
            tbody: '',
            tr: '',
            th: 'py-4 font-normal text-start border-b border-default',
            td: 'px-6 py-4 text-center border-b border-default',
            tier: 'p-6 text-start font-normal align-top h-full',
            tierWrapper: 'flex flex-col md:h-full',
            tierTitleWrapper: 'flex items-center gap-3',
            tierTitle: 'text-lg font-semibold text-highlighted',
            tierDescription: 'text-sm font-normal text-muted mt-1',
            tierBadge: 'truncate',
            tierPriceWrapper: 'flex items-center gap-1 mt-4',
            tierPrice: 'text-highlighted text-3xl sm:text-4xl font-semibold',
            tierDiscount: 'text-muted line-through text-xl sm:text-2xl',
            tierBilling: 'flex flex-col justify-between min-w-0',
            tierBillingPeriod: 'text-toned truncate text-xs font-medium',
            tierBillingCycle: 'text-muted truncate text-xs font-medium',
            tierButton: 'mt-6 md:mt-auto md:pt-6',
            tierFeatureIcon: 'size-5 shrink-0',
            section: 'mt-6 flex flex-col gap-2',
            sectionTitle: 'font-semibold text-sm text-highlighted',
            feature: 'flex items-center justify-between gap-1',
            featureTitle: 'text-sm text-default',
            featureValue: 'text-sm text-muted flex justify-center min-w-5'
          },
          variants: {
            section: {
              true: {
                tr: '*:pt-8'
              }
            },
            active: {
              true: {
                tierFeatureIcon: 'text-primary'
              }
            },
            highlight: {
              true: {
                tier: 'bg-elevated/50 border-x border-t border-default rounded-t-lg',
                td: 'bg-elevated/50 border-x border-default',
                item: 'bg-elevated/50'
              }
            }
          }
        }
      }
    })
  ]
})

Changelog

No recent changes