Pricing Tier Configurator
The user adjusts inputs (seats, usage, features) and the component highlights one of N pricing tiers as recommended, updating in real time. Composes PrimaryButtonCTA.
Live preview
Rendered with real component code. Each example demonstrates a documented variant.
SaaS pricing
Find your plan
Adjust the inputs below and we will highlight the right tier.
Starter
For individuals and small projects.
- Up to 5 seats
- 10 GB storage
- Email support
- Core integrations
Pro
For growing teams who need advanced tooling.
- Up to 30 seats
- 100 GB storage
- Priority support
- Advanced analytics
- All integrations
Enterprise
Custom controls and dedicated support at scale.
- Unlimited seats
- Unlimited storage
- Dedicated success manager
- SSO and audit logs
- Custom SLA
Vertical layout
Find your plan
Adjust the inputs below and we will highlight the right tier.
Starter
For individuals and small projects.
- Up to 5 seats
- 10 GB storage
- Email support
- Core integrations
Pro
For growing teams who need advanced tooling.
- Up to 30 seats
- 100 GB storage
- Priority support
- Advanced analytics
- All integrations
Enterprise
Custom controls and dedicated support at scale.
- Unlimited seats
- Unlimited storage
- Dedicated success manager
- SSO and audit logs
- Custom SLA
React props
Configure the component via props in React or via attributes in HTML.
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | undefined | Section heading rendered above the inputs and tiers |
| description | string | undefined | Supporting copy below the heading |
| tiers | Tier[] | required | Pricing tier definitions; each tier has slug, name, price, features, and cta |
| inputs | Input[] | required | Inputs the user adjusts; supports slider, select, and checkbox types |
| recommend | (values: Record<string, number | string | boolean>) => string | required | Returns the slug of the recommended tier based on current input values |
| layout | "horizontal" | "vertical" | "horizontal" | Tier layout direction; horizontal uses auto-fit grid columns, vertical stacks tiers |
| onTierChange | (slug: string) => void | undefined | Called when the recommended tier slug changes |
| className | string | undefined | Extra class applied to the root element |
HTML usage
The HTML variant uses the same shared styles and class names. Drop into any stack.
<link rel="stylesheet" href="path/to/pricing-tier-configurator/styles.css">
<section class="ptc ptc--horizontal" id="pricing-configurator">
<header class="ptc__header">
<h2 class="ptc__title">Find your plan</h2>
<p class="ptc__description">Adjust the inputs and we will highlight the right tier.</p>
</header>
<div class="ptc__inputs" aria-live="polite">
<div class="ptc__input-field">
<label class="ptc__input-label" for="ptc-seats">How many seats do you need?</label>
<div class="ptc__input-row">
<input id="ptc-seats" class="ptc__slider" type="range" min="1" max="100" step="1" value="5">
<span class="ptc__slider-value" id="ptc-seats-value">5</span>
</div>
</div>
<div class="ptc__input-field">
<div class="ptc__checkbox-row">
<input id="ptc-analytics" class="ptc__checkbox" type="checkbox">
<label class="ptc__input-label" for="ptc-analytics">Need advanced analytics?</label>
</div>
</div>
<div class="ptc__input-field">
<div class="ptc__checkbox-row">
<input id="ptc-sso" class="ptc__checkbox" type="checkbox">
<label class="ptc__input-label" for="ptc-sso">Need SSO or audit logs?</label>
</div>
</div>
</div>
<div class="ptc__tiers" id="ptc-tiers">
<!-- Tiers rendered by script below -->
</div>
</section>
<script>
(function () {
var seats = document.getElementById('ptc-seats');
var seatsVal = document.getElementById('ptc-seats-value');
var analytics = document.getElementById('ptc-analytics');
var sso = document.getElementById('ptc-sso');
var tiersEl = document.getElementById('ptc-tiers');
var tiers = [
{ slug: 'starter', name: 'Starter', price: '$19/mo',
features: ['Up to 5 seats', '10 GB storage', 'Email support'],
cta: { label: 'Choose Starter', href: '/signup?plan=starter' } },
{ slug: 'pro', name: 'Pro', price: '$79/mo',
features: ['Up to 30 seats', '100 GB storage', 'Priority support', 'Advanced analytics'],
cta: { label: 'Choose Pro', href: '/signup?plan=pro' }, badge: 'Most popular' },
{ slug: 'enterprise', name: 'Enterprise', price: 'Custom',
features: ['Unlimited seats', 'Unlimited storage', 'SSO and audit logs'],
cta: { label: 'Contact sales', href: '/contact/sales' } },
];
function recommend() {
var s = Number(seats.value);
if (sso.checked) return 'enterprise';
if (s > 30 || analytics.checked) return 'pro';
return 'starter';
}
function render() {
var rec = recommend();
seatsVal.textContent = seats.value;
tiersEl.innerHTML = tiers.map(function (t) {
var cls = 'ptc__tier' + (t.slug === rec ? ' ptc__tier--recommended' : '');
return '<div class="' + cls + '">'
+ (t.badge ? '<span class="ptc__badge">' + t.badge + '</span>' : '')
+ '<h3 class="ptc__tier-name">' + t.name + '</h3>'
+ '<div class="ptc__tier-price">' + t.price + '</div>'
+ '<ul class="ptc__tier-features">' + t.features.map(function (f) {
return '<li>' + f + '</li>';
}).join('') + '</ul>'
+ '<a class="pbc pbc--' + (t.slug === rec ? 'solid' : 'outlined') + ' pbc--rounded"'
+ ' href="' + t.cta.href + '">' + t.cta.label + '</a>'
+ '</div>';
}).join('');
}
seats.addEventListener('input', render);
analytics.addEventListener('change', render);
sso.addEventListener('change', render);
render();
}());
</script>Customization
Override these CSS custom properties to integrate the component into your brand.
.ptc {
--ptc-bg: var(--brand-surface, white);
--ptc-text-color: var(--brand-ink, #102542);
--ptc-muted-color: rgba(16, 37, 66, 0.6);
--ptc-accent: var(--brand-accent, #1e5fcf);
--ptc-tier-bg: white;
--ptc-tier-border: rgba(0, 0, 0, 0.1);
--ptc-tier-recommended-bg: rgba(30, 95, 207, 0.04);
--ptc-tier-recommended-border: var(--ptc-accent);
--ptc-tier-recommended-shadow: 0 8px 32px -8px rgba(30, 95, 207, 0.25);
--ptc-input-bg: rgba(0, 0, 0, 0.025);
--ptc-input-border: rgba(0, 0, 0, 0.12);
--ptc-badge-bg: var(--ptc-accent);
--ptc-badge-text: white;
--ptc-radius: 1rem;
--ptc-section-padding: 2rem;
--ptc-gap: 1.5rem;
}