Plan pricing for SaaS subscriptions, agency packages and retainers. Plans are separated by hairline rules, not cards; the highlighted plan gets a 2px accent rule and a label (no glow, no scale). The monthly/yearly switch is a fieldset of hidden radios read by :has() — zero JavaScript. Money goes through Intl per locale and currency, and a tax note is required.
Pricing
One booking page, three sizes
Every plan includes the booking page, WhatsApp confirmations and a Google Business Profile sync. Change or cancel any month.
Solo
One practitioner, one calendar.
29 US dollars /mo
290 US dollars /yr
Start with Solo- 1 calendar
- Booking page and widget
- WhatsApp remindersUp to 300 a month
- Not included: Deposits and prepayment
Studio
Most chosenA salon, spa or clinic with a front desk.
79 US dollars /mo
790 US dollars /yr
Start with Studio- Up to 8 calendars
- Deposits and prepayment
- WhatsApp remindersUp to 2,000 a month
- Gift cards
Group
Several branches under one brand.
Custom
Talk to sales- Unlimited branches
- Central reporting
- SSO and audit log
- A named account manager
Prices exclude 20% VAT
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { PricingTable } from "@vamos-ui/react/blocks";
<PricingTable
title="One booking page, three *sizes*"
billing="both"
yearlySaving="2 months free"
taxNote="Prices exclude 20% VAT"
plans={[
{ name: "Solo", price: { monthly: { amount: "29", currency: "USD" }, yearly: { amount: "290", currency: "USD" } },
features: [{ text: "1 calendar" }, { text: "Deposits", included: false }],
cta: { link: { href: "/signup?plan=solo", label: "Start with Solo" }, style: "secondary" } },
{ name: "Studio", highlight: "Most chosen", … },
]}
/>The schema (`pricingBlock` in blocks/site/registry-2.ts) emits an OfferCatalog with a UnitPriceSpecification per period.
Examples
Rows (agency packages, Turkish)
Each package reads like a line on a quote: name and description, a comma list of inclusions, price and action. ₺ formatting and the “/ay” suffix come from Intl and site2LabelsTr.
Paketler
Web sitesi ve bakım
Tanıtım sitesi
Tek sayfa, iki dil, rezervasyon ve harita.
Tasarım, Türkçe ve İngilizce metin, Google İşletme Profili kurulumu
başlayanbaşlayan ₺45.000 Türk lirası tek sefer
Teklif alKurumsal site
En çok tercih edilen20 sayfaya kadar, blog ve menü yönetimi.
İçerik yönetimi, Blog, SEO denetimi, Eğitim
başlayanbaşlayan ₺120.000 Türk lirası tek sefer
Teklif alAylık bakım
Güncelleme, yedekleme ve küçük değişiklikler.
Aylık 4 saat değişiklik, Günlük yedek, Uptime takibi
Fiyatlara KDV dahil değildir
Single package (ad retainer)
One plan: large price and action on the start side, a two-column inclusion list, and a “Not included” line.
Ad management
Google Ads, handled
Local retainer
For restaurants, spas and clinics spending $1,000–5,000 a month on Google Ads.
650 US dollars /mo
Book a 20-minute callIncluded
- Search and Performance Max campaigns
- Call and booking conversion tracking
- Weekly search-term cleanup
- Monthly report in plain language
- Landing page changesUp to 2 hours a month
- Google Business Profile posts
Not included: Meta and TikTok ads, Creative production
Ad spend is billed by Google, not included
Quote-only plans
No price: `custom` text (“From $4,500”, “Get a quote”) takes its place.
Built to order
Every project starts with a free 30-minute call and a written quote within two working days.
Website
Design, build and launch in 4–8 weeks.
From $4,500
Request a quote- Up to 20 pages
- Two languages
- Booking or contact flows
Booking system
Calendars, deposits and reminders for multi-branch businesses.
Get a quote
Request a quote- Staff and room calendars
- iyzico deposits
- WhatsApp reminders
Quotes exclude VAT
API
PricingTable
| Prop | Type | Default |
|---|---|---|
variantLayout. A single plan always renders as single. | "columns" | "rows" | "single" | "columns" |
plans1–4 plans (see below). | PricingPlan[] | — |
billingWhich prices exist. both adds the period switch; once shows one-off prices. | "monthly" | "yearly" | "both" | "once" | "monthly" |
defaultPeriodSelected period with billing=both. | "monthly" | "yearly" | "monthly" |
yearlySavingNext to “Yearly”: “2 months free” (text, never a strikethrough). | ReactNode | — |
taxNoteRequired by the house rules: “Prices exclude 20% VAT”. | ReactNode | — |
footnoteTerms under the table. | ReactNode | RichDoc | — |
eyebrow / title / leadSection header; title supports one *emphasis*. | ReactNode | — |
labelsBuilt-in strings (Monthly, /mo, Not included…). site2LabelsTr for Turkish. | Partial<Site2Labels> | — |
localeIntl locale for money. | string | "en-US" |
theme / anchor / id / className / headingLevel / components / editModeShared block props. | BlockBaseProps | — |
PricingPlan
| Prop | Type | Default |
|---|---|---|
name / descriptionPlan name (h3) and one line. | ReactNode | — |
priceMoney is { amount: decimal string, currency: ISO 4217 }. from prefixes “from”. | { monthly?: Money; yearly?: Money; once?: Money; from?: boolean } | — |
customShown instead of a price. | ReactNode | — |
featuresUp to 14. Excluded features are muted with a dash and read “Not included”. | { text; included?; note? }[] | — |
ctaOne primary per block: the highlighted plan keeps it, other primaries become secondary. | { link: { href, label, newTab? }; style? } | — |
highlightLabel text for the recommended plan. | ReactNode | — |