Pricing Snippets — Free HTML CSS JS Copy-Paste Pricing Components
14 pricing components · Pricing Page, Toggle, Card, Calculator, Upgrade Banner, FAQ, Enterprise, Trial Countdown, Guarantee · Exports to React, Vue, Angular & Tailwind
What's included
Features
About this tool
Pricing Snippets — Free, Complete SaaS Pricing UI From Plans to Upgrade Prompts
Pricing pages are consistently among the highest-converting pages on any SaaS or product site. The design of your pricing UI — the plan layout, the billing toggle, the feature comparison, the upgrade prompt — directly affects whether users choose to pay. This collection covers every pricing UI pattern you will encounter when building a real product.
Every snippet is plain HTML, CSS, and vanilla JavaScript. No React, no npm, no build step. Copy the code and paste it into any project.
Pricing Page is a complete three-tier SaaS pricing section. The Starter, Pro, and Team plan columns use a CSS grid with repeat(3,1fr) and align-items: start so cards sit at natural heights. The billing toggle uses two JavaScript arrays (monthly prices and annual prices) swapped on click. A CSS knob slides right via transform: translateX when the .annual class is applied. Feature list items use inline SVG data URIs on ::before pseudo-elements for check (green) and cross (grey with line-through) icons — no icon library required. A guarantee row below the plans handles the "no credit card, cancel anytime" trust signal.
Pricing Card provides a focused two-tier comparison (Free vs Pro) for in-context plan comparisons. The popular badge uses position: absolute; top: -12px; left: 50%; transform: translateX(-50%) to float above the featured card header. The featured card uses an accent border and coloured box-shadow glow.
Pricing Toggle is a standalone monthly/annual billing switcher — useful when you already have a pricing table and just need the toggle. The knob, label colour changes, and price array swap are all identical to the Pricing Page but as a separate, composable component.
Usage Pricing Calculator solves the pricing anxiety problem for metered and usage-based billing. Three sliders (API calls, storage, seats) use non-linear lookup arrays so a single drag step covers a meaningful usage tier rather than a linear percentage. The live cost breakdown updates all line items and the total on every input event. Custom range slider thumbs use WebKit and Mozilla pseudo-elements for cross-browser brand consistency.
Upgrade Banner provides two in-app upsell components: a gradient dismissible top banner with a CSS @keyframes collapse dismiss animation, and a locked feature gate with a 1.5px dashed border. The feature gate shows users exactly what they are missing — research consistently shows this converts at a higher rate than hiding the feature entirely. The banner dismiss uses max-height animation to collapse without needing JavaScript height calculation.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Store two Stripe priceId values per plan — one for monthly and one for annual: const plans = [{monthlyId:"price_xxx", annualId:"price_yyy"}, ...]. On CTA click, read the isAnnual boolean and select the right ID: stripe.redirectToCheckout({priceId: isAnnual ? plans[i].annualId : plans[i].monthlyId}). Add data-plan-index to each CTA button for easy lookup without hardcoding IDs inline.
Add a fourth .plan div inside .plans in the HTML, matching the structure of the existing three columns. Add a fourth price to both the monthly and annual JS arrays at index 3. Change the CSS grid to grid-template-columns: repeat(4,1fr). For responsive behaviour, use repeat(auto-fill, minmax(220px,1fr)) — the browser wraps to a new row on narrow screens automatically.
apiTiers and storageTicks are lookup arrays where each position represents a real usage tier — [0, 1000, 5000, 10000, 25000, 50000, 100000, 250000, 500000, 1000000, 5000000] for API calls. The HTML range input spans 0–10. Math.round(sliderValue * (array.length - 1) / 10) converts the 0–10 position to an array index. Each drag step moves to the next tier rather than incrementing linearly.
Inside dismiss() in the JS panel, add localStorage.setItem("upgrade_dismissed", Date.now()) before the animation starts. On every page load, read: const stored = localStorage.getItem("upgrade_dismissed"); const TTL = 7 * 24 * 60 * 60 * 1000; if (stored && Date.now() - parseInt(stored) < TTL) { banner.style.display = "none"; return; } Adjust the TTL constant to control how many days before the banner reappears for the same user.