Shadcn Pricing Page: Jahres-Toggle, Tabelle, Mobile-Layout

Eine shadcn pricing page wirkt einfach, bis du eine baust: ein Umschalter Monatlich / Jährlich, drei Plan-Karten, eine Vergleichstabelle, Buttons, die einen Checkout starten. shadcn/ui selbst liefert Stand Oktober 2026 keinen Pricing-Block, also starten die meisten mit einer Galerie. shadcnblocks (opens in a new tab) listet fünf Layouts für Pricing Pages, shadcn/studio (opens in a new tab) listet zwanzig, davon eins als kostenlos markiert, und 21st.dev hostet Preistabellen aus der Community wie die von Kokonut UI (opens in a new tab), die Preise mit NumberFlow animiert (alles geprüft am 16. Oktober 2026). Was diese Seiten selten erklären, ist der Teil, der kaputtgeht: die Rechnung beim Jahrespreis, was die Vergleichstabelle auf einem 390px breiten Bildschirm macht und was ein Screenreader hört, wenn sich die Preise ändern.
In diesem Tutorial baust du eine React Pricing Page mit der Pricing Table von Wingo UI. Wir entwickeln diese Bibliothek selbst, sind also nicht neutral. Jedes Snippet nutzt die echten Props der Komponente, und jede Angabe von außen ist mit ihrer Quelle verlinkt.
Was braucht eine shadcn pricing page?
Eine shadcn pricing page braucht fünf Teile, in dieser Reihenfolge:
- Einen Umschalter für den Zeitraum. Monatlich und Jährlich, mit der Ersparnis an der Option Jährlich. Er ändert Preise, die schon zu sehen sind, also ist er eine Radio Group; Tabs wechseln dagegen ganze Panels.
- Plan-Karten. Zwei bis vier Pläne, einer hervorgehoben. Jede Karte hat einen Namen, eine Zeile „für wen“, einen Preis, eine kurze Feature-Liste und einen Button.
- Eine Vergleichstabelle. Jedes Feature in einer Zeile, die Pläne in Spalten. Die Richtlinien für Vergleichstabellen (opens in a new tab) der Nielsen Norman Group empfehlen höchstens etwa fünf Elemente, für jedes Element dieselben Merkmale und einen Header, der beim Scrollen langer Tabellen sichtbar bleibt.
- Buttons, die zum Checkout führen. Ein Button, der zeigt, dass er arbeitet, während dein Server die Checkout-Session anlegt.
- Antworten auf Einwände. Ein FAQ und ein abschließender Call to Action. Diese gehören zur Seite rund um die Tabelle.
Baust du das aus Teilen von shadcn/ui, greifst du zu Card, Toggle Group oder Tabs und Table und schreibst dann die Preisformatierung, die Ersparnis-Rechnung, die Ansage für den Screenreader und das Layout fürs Smartphone selbst. Der Rest dieses Artikels nutzt eine React Pricing Table Component, die das schon erledigt und sich wie jede shadcn-Komponente als Quellcode installiert. Sie deckt die ersten vier Teile in einer Datei ab.
Wie installierst du die Preistabelle?
Ein einziger CLI-Befehl kopiert diese shadcn pricing table und alles, was sie importiert, in dein Projekt:
Das CLI kopiert components/ui/pricing-table.tsx als Quellcode in dein Projekt, dazu alles, was die Datei importiert: das Segmented Control für den Umschalter, Price für die rollenden Ziffern, Carousel fürs Smartphone, Table für den Vergleich und die gemeinsamen Helfer für Tones, Motion und Locale. Außerdem installiert es motion und lucide-react. Die Datei ist eine Client-Komponente ("use client") und funktioniert deshalb auch in einer Seite im App Router.
Wie modellierst du Pläne und Jahrespreise?
Pläne sind einfache Daten. Ein Preis ist eine Zahl, wenn er sich nie ändert, { monthly, yearly }, wenn er dem Umschalter folgt, 0 für kostenlos und null für einen individuellen Plan (sein Button heißt standardmäßig „Contact us“).
Die eine Entscheidung, die sitzen muss: yearly ist das, was die Kundin oder der Kunde pro Jahr zahlt, der volle Jahresbetrag. Speicher die Zahl, die dein Billing-Anbieter abbucht, und lass die Komponente teilen.
Mit diesen Zahlen zeigt die Tabelle bei Yearly den Plan Starter mit $12/mo mit „Billed $144 yearly“ darunter und Team als $32/mo mit „Billed $384 yearly“. Die Option Yearly bekommt ein Badge „Save 20%“: Die Komponente rechnet für jeden Plan 1 - yearly / (monthly * 12), rundet ab und zeigt den besten Wert. Willst du lieber den Jahresbetrag auf der Karte, setz yearlyDisplay="per-year", dann steht darunter „You save $36“.
Ein Feature ist entweder ein String (enthalten) oder { label, included?, hint? }. included: false zeigt es als fehlend, und ein hint ergänzt einen (i)-Button, der sich bei Hover, Fokus oder Tippen öffnet.
Zum Rendern reicht ein Element:
Diese Seite kann eine Server-Komponente bleiben: Die Pläne sind einfache Daten, und PricingTable ist die Client-Grenze.
$ npx wingo-ui@latest add pricing-tableWie funktioniert der Umschalter Monatlich / Jährlich?
Der Pricing Toggle ist das kostenlose Segmented Control, gerendert mit aria-label="Billing period". Es ist eine Radio Group mit einem einzigen Tab-Stopp: Die Pfeiltasten Links und Rechts verschieben die Auswahl und wählen dabei aus, und auf Touch-Geräten hältst du den Thumb gedrückt und ziehst ihn hinüber. Wir erklären das Control ausführlich, auch wann Tabs oder eine Toggle Group besser passen, in unserem Guide zum React Segmented Control.
Drei Details zählen auf einer Pricing Page:
- Die Preise rollen. Jede Ziffer rollt zu ihrem neuen Wert, wenn sich der Zeitraum ändert. Ist „Bewegung reduzieren“ aktiv, erscheint der neue Wert sofort. Setz
animatePrice={false}, um das Rollen für alle abzuschalten. - Ein Screenreader hört die Änderung einmal. Eine Live Region (polite) sagt den Zeitraum und den neuen Preis des hervorgehobenen Plans an, zum Beispiel „Yearly. Starter: $12 per month, Billed $144 yearly“. Beim ersten Rendern bleibt sie still, damit der Seitenaufbau nicht doppelt vorgelesen wird.
- Der Umschalter blendet sich selbst aus, wenn kein Plan einen Jahrespreis hat. Mit
showToggle={false}blendest du ihn bewusst aus.
Die Komponente startet auf Monthly. Soll sie auf Yearly starten, ohne dass du State verwaltest, gib defaultPeriod="yearly" mit. Um den Zeitraum von außen zu lesen (für Checkout oder Analytics), steuerst du ihn mit period und onPeriodChange, typisiert mit dem exportierten BillingPeriod. Der Abschnitt zum Checkout weiter unten zeigt die komplette gesteuerte Version.
Wie fügst du die Vergleichstabelle hinzu?
Gib comparison als Gruppen von Zeilen mit. Jede Zeile hat einen Wert pro Plan-ID: true ist ein Haken, false ein Strich, und alles andere wird als Text ausgegeben. Ergänze sie in derselben plans.ts, mit den Plan-IDs von oben als Schlüssel:
Fehlt eine Plan-ID in values, erscheint ein Strich, genau wie bei false. Ein Tippfehler oder eine umbenannte Plan-ID erscheint also ohne jede Warnung als „nicht enthalten“. Halte die Schlüssel synchron mit plans. In einem breiten Layout rendert diese Vergleichstabelle als echte <table>, deren Kopfzeile mit Plan-Namen und Buttons beim Scrollen kleben bleibt. Das ist der Sticky Header, den der Artikel von NN/g verlangt. Hat deine Seite eine fixierte Top-Bar, setz die CSS-Variable --topbar-offset auf einem Elternelement auf deren Höhe, damit der Sticky Header darunter stoppt statt dahinter.
Wie ordnest du die Pläne auf dem Smartphone an?
Auf dem Smartphone werden die Pläne zu wischbaren Karten, und der Vergleich zeigt einen Plan nach dem anderen. Nur Stapeln reicht nicht: Drei gestapelte Plan-Karten schieben den Vergleich auf dem Smartphone mehrere Bildschirmhöhen nach unten, und eine Vergleichstabelle mit vier Spalten bei 390px quetscht entweder die Beschriftungen oder scrollt seitlich und verliert sie.
Die Pricing Table wechselt das Layout bei 768px ihrer eigenen Breite, gemessen mit Container Queries statt am Viewport. Sie ordnet sich also auch in einer schmalen Doku-Spalte oder neben einer Sidebar richtig an, und weil das reines CSS ist, hat schon das HTML vom Server das richtige Layout, ohne Flackern bei der Hydration.
Unter 768px:
- Pläne werden zu wischbaren Karten, die beim hervorgehobenen Plan starten, die nächste Karte schaut am Rand hervor, und Punkte springen zwischen den Karten. Die Pfeiltasten Links und Rechts funktionieren, sobald der Fokus darin liegt. Bei zwei Plänen setzt
mobileLayout="stack"sie stattdessen untereinander. - Der Vergleich wird zur Plan-Auswahl (ein weiteres Segmented Control) über einer Spalte mit den Zeilen des gewählten Plans, am Ende mit dessen Button. Das ist derselbe Schritt wie bei einer Tabelle, die zu Karten wird; für Datentabellen zeigen wir ihn in responsive Tabellen als mobile Karten.
- Der Umschalter nimmt die volle Breite ein, und auf Touchscreens ist er 44px hoch.
Die schmale Spalte auf der Doku-Seite zeigt das Smartphone-Layout, so kannst du beides ausprobieren: Öffne die Doku der Pricing Table am Laptop und auf dem Smartphone.
Wie verbindest du die Buttons mit dem Checkout?
Das cta eines Plans nimmt entweder ein href (rendert einen Link) oder ein onClick. Gibt onClick ein Promise zurück, zeigt der Button dieses Plans einen Spinner, bis es erledigt ist, und weiteres Tippen wird ignoriert. Hier ist die fertige Seite, mit gesteuertem Zeitraum und einem Checkout über deine eigene API-Route für jeden bezahlten Plan:
Funktionen können nicht von einer Server-Komponente an eine Client-Komponente übergeben werden, deshalb stehen die Pläne mit onClick in pricing-plans.tsx. Hat jeder Plan nur ein href, kannst du PricingTable direkt aus der Server-Seite rendern.
tone="brand" färbt Badge und Button des hervorgehobenen Plans, und jeder Tone außer neutral macht die Feature-Haken grün. Standard ist neutral, dann bleiben die Haken in der Textfarbe, und color nimmt jede CSS-Farbe, falls deine Marke keinem der Tones entspricht. featuredStyle legt fest, wie der hervorgehobene Plan auffällt: raised (Standard), outline oder inverted.
Willst du lieber die ganze Pricing Page?
Willst du die komplette Route und nicht nur die Tabelle, installier den Block Pricing Page. Er bettet die Tabelle in eine komplette Seite ein: mit Marketing-Header, dessen Menü auf dem Smartphone als Bottom Sheet aufgeht, den Plänen samt Umschalter Monatlich / Jährlich und Umschalter USD / EUR, Kundenlogos, drei Zitaten, einem FAQ-Akkordeon, einem abschließenden Call to Action und einem Footer.
Er bringt Beispieldaten mit (PRICING_PAGE_SAMPLE) und sieht deshalb ganz ohne Props fertig aus. Gib eigene plans, comparison, faqs und testimonials mit, um sie zu ersetzen, und schalte Abschnitte mit showLogos, showTestimonials, showFaq oder showCta ab. Euro-Preise werden mit eurRate (standardmäßig 1.08) aus deinen Basispreisen umgerechnet und auf ganze Euro gerundet. Die Umrechnung läuft immer, wenn die Währung EUR ist. Gibst du Pläne mit, die schon in Euro bepreist sind, setz also zusätzlich currency="EUR", currencies={["EUR"]} (ein einziger Eintrag blendet den Umschalter aus) und eurRate={1}, sonst werden deine Euro-Preise ein zweites Mal geteilt. design="bold" tauscht den klassischen Look gegen schwere Display-Schrift und einen invertierten hervorgehobenen Plan. Setz header={false} und footer={false}, wenn die Seite in deinem eigenen Site-Layout sitzt.
$ npx wingo-ui@latest add pricing-pageDieser Block ersetzt die zwei Dateien aus dem vorigen Abschnitt: Nimm für /pricing das eine oder das andere. Weil diese page.tsx eine Client-Komponente ist, kann sie kein metadata exportieren. Brauchst du einen Seitentitel, verschieb das Element PricingPage in eine eigene Client-Datei und lass page.tsx eine Server-Komponente bleiben, wie im vorigen Abschnitt.
Für einen Preisabschnitt auf einer Landing Page statt einer ganzen Route ist die Pricing Section die kompakte Version mit vier Layouts und einem Link „Compare all features“. Ein Unterschied, auf den du achten musst: Dort ist der Preis yearly der Monatswert bei jährlicher Zahlung, während yearly in der Pricing Table der Jahresbetrag ist.
Was ist der nächste Schritt?
Installier die Tabelle mit npx wingo-ui@latest add pricing-table, füg deine Pläne ein und prüf sie zuerst bei 390px. Die Pricing Table und die Pricing Page gehören zu Wingo UI Pro; das Segmented Control für den Umschalter ist kostenlos. Für die anderen Screens einer SaaS-App starte mit unserem Tutorial für ein Next.js Admin Dashboard oder stöber in der restlichen Kategorie Praxis: Screens und Apps.
Häufige Fragen
Hat shadcn/ui eine Pricing Page oder eine Preistabelle?
Stand Oktober 2026 nicht. Die offizielle Blocks-Seite zeigt Blöcke für Dashboard, Sidebar, Login und Signup, und die Komponentenliste hat Card, Table, Tabs und Toggle Group, aber keine Pricing-Komponente. Pricing Pages stammen also aus Community-Galerien, oder du baust sie selbst aus diesen Teilen.
Soll ein Jahresplan den Monatswert oder den Jahresbetrag zeigen?
Zeig den Monatswert und darunter den Jahresbetrag („$12/mo, Billed $144 yearly“), damit sich die Zahl direkt mit dem Monatspreis vergleichen lässt. Die Pricing Table von Wingo UI macht das standardmäßig und zeigt mit yearlyDisplay="per-year" stattdessen den Jahresbetrag und die Ersparnis.
Wie wird die prozentuale Ersparnis beim Jahresplan berechnet?
Rechne 1 minus Jahrespreis geteilt durch zwölf Monatszahlungen und runde ab. $144 im Jahr gegenüber $15 im Monat ergibt 1 - 144 / 180, also 20 %. Die Pricing Table zeigt im Badge an der Option Yearly die beste Ersparnis über alle Pläne.
Wie mache ich eine Preistabelle auf dem Smartphone responsive?
Mach aus den Plänen eine wischbare Reihe von Karten, die beim hervorgehobenen Plan startet, oder stapel sie untereinander, wenn es nur zwei sind. Die Vergleichstabelle ersetzt du durch eine Plan-Auswahl über einer einzigen Spalte mit Zeilen. Breite Vergleichstabellen, die auf einem 390px breiten Bildschirm seitlich scrollen, verlieren die Zeilenbeschriftungen.
Ist die Pricing Table von Wingo UI kostenlos?
Das Segmented Control für den Umschalter ist kostenlos. Die Pricing Table, die Pricing Section und der komplette Block Pricing Page gehören zu Wingo UI Pro, und alle lassen sich mit npx wingo-ui@latest add installieren.
- Preise
- React
- shadcn/ui
- Next.js
- Tailwind CSS


