Preisliste

GitHub
Eine responsive Preistabellenkomponente, die abgestufte Preispläne mit Feature-Vergleichen anzeigt.

Bearbeiten

Die PricingTable-Komponente bietet eine reaktionsschnelle und anpassbare Möglichkeit, Preispläne in einem Tabellenformat anzuzeigen und automatisch zwischen einem horizontalen Tabellenlayout auf dem Desktop für einen einfachen Vergleich und einem vertikalen Kartenlayout auf dem Handy für eine bessere Lesbarkeit zu wechseln.

  • 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

Tiers (englisch)

Verwenden Sie die tiers-prop als Array von Objekten, um Ihre Preispläne zu definieren. Jedes Tier-Objekt unterstützt die folgenden Eigenschaften:

  • id: string-Eindeutige Kennung für die Ebene (erforderlich)
  • title?: string-Name des Preisplans
  • description?: string-Kurzbeschreibung des Plans
  • price?: string-Der aktuelle Preis des Plans (z. B."99 $","99 €","Free")
  • discount?: string-Der ermäßigte Preis, der den price mit Durchstreichen anzeigt (z. B."$79","€ 79")
  • billingCycle?: string-Der Einheitspreiszeitraum, der neben dem Preis angezeigt wird (z. B."/Monat","/Sitz/Monat")
  • billingPeriod?: string-Zusätzlicher Abrechnungskontext, der über dem Abrechnungszeitraum angezeigt wird (z. B."monatlich in Rechnung gestellt")
  • badge?: string | BadgeProps-Zeigt ein Abzeichen neben dem Titel { color: 'primary', variant: 'subtle' } an
  • button?: ButtonProps-Konfigurieren der CTA-Taste { size: 'lg', block: true }
  • highlight?: boolean-Ob diese Ebene als empfohlene Option visuell hervorgehoben werden soll
  • 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 Bearbeiten

Verwenden Sie die sections-prop zu organisieren features in logische groups. Each Abschnitt stellt eine Kategorie von features, die Sie vergleichen möchten, über verschiedene Preisstufen.

  • title: string-Die Überschrift für den Feature Abschnitt
  • features: PricingTableSectionFeature[]-Eine Reihe von Funktionen mit ihrer Verfügbarkeit in jeder Ebene:
    • Jedes Feature erfordert eine title-und eine tiers-Objekt-Zuordnungs-Tier-IDs zu Werten
    • Boolesche Werte (true/false) werden als Häkchen (✓) oder Minuszeichen (-) angezeigt.
    • String-Werte werden als Text angezeigt (z.B."Unbegrenzt","Bis zu 5 Benutzer")
    • Numerische Werte werden so angezeigt wie sie sind (z.B. 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>

Examples (Beispiele)

Mit Steckplätze

Die PricingTable-Komponente bietet leistungsstarke Slot-Anpassungsoptionen, um die Anzeige Ihrer Inhalte anzupassen. Sie können einzelne Elemente mit generischen Slots anpassen oder bestimmte Elemente mit ihren IDs ansprechen.

  • 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>

Die Komponente unterstützt verschiedene Slottypen für maximale Anpassungsflexibilität:

Der Slot TyppatternDescriptionbeispiel
Tier slots (Deutsche Übersetzung)x235x BearbeitenSpezifische Ziele für Dritte#team-title, #solo-price, #team-title und #solo-price
Section slots (Deutsche Ausgabe)x240x BearbeitenSpezifische Target-Abschnittex241x Bearbeiten
Feature slotsX244x BearbeitenSpezifische TargetsX245x Bearbeiten
Generic slots (Deutsche Übersetzung)#tier-title, #section-title, usw.Gilt für alle ItemsXP250X Bearbeiten
Wenn kein id angegeben ist, wird der Slot-Name automatisch aus dem Titel generiert (z. B. wird aus "Premium Features!" #section-premium-features-title).

API Bearbeiten

Props (nicht)

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; }

Slots Bearbeiten

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 Bearbeiten

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 (englisch)

No recent changes