Why Did I Build a Pricing Calculator Inside Webflow CMS Instead of Using a Third-Party Tool?
A B2B SaaS client of mine sells four packages with three add-ons each. The pricing is easy to read but hard to model. They were using Outgrow for an interactive calculator, paying about 89 dollars a month for a feature their buyers used roughly 600 times a quarter. The embed loaded slowly, the styling did not match the brand, and the leads it generated landed in a separate inbox the sales team forgot to check.
In March 2026 I moved the whole calculator into Webflow CMS, with a small Webflow Logic flow at the back, and removed the Outgrow embed entirely. The calculator now lives at the bottom of the pricing page and runs on the same domain. According to the client's own HubSpot data, the conversion rate from calculator view to demo request rose by 22 percent in the eight weeks after the move.
I want to walk through how the calculator works, why CMS is the right place for it, and what to watch for if you build one yourself in 2026.
What Does a Webflow CMS-Driven Pricing Calculator Actually Look Like Under the Hood?
The structure is simple. There is a Plans collection that holds each base package, with fields for the package name, monthly price, included seats, and a unit price for additional seats. There is an Add-Ons collection that holds optional features, each with a name, a price, and a reference field to the plans they apply to. There is a JavaScript layer that reads the visible selections, looks up the matching CMS data, and updates the total in the DOM.
The CMS holds the source of truth. The JavaScript is small, about 90 lines, and it never hard-codes a price. When the client wants to change a plan price, they update the CMS item, hit publish, and the calculator updates everywhere it appears. That includes the pricing page, any blog post that embeds it, and any marketing email that links to a deep-linked calculator state.
The pattern is close to what I described in my walk-through of building a Webflow CMS tag filter without Finsweet. The principle is the same. Let the CMS hold the data and let a small script handle the math.
How Do You Set Up the Webflow CMS Collections for This?
You need two collections. The first is Plans. Add a Plain Text field for the plan name, a Number field for the monthly base price, a Number field for the included seats, a Number field for the price per additional seat, and a Switch field for "is most popular." That last one drives a badge on the UI.
The second is Add-Ons. Add a Plain Text field for the add-on name, a Number field for the monthly price, a Multi-Reference field pointing at Plans for compatibility, and a Plain Text help-text field for the tooltip. The Multi-Reference is what makes the calculator behave correctly when a buyer picks the wrong plan for the add-on they want.
The CMS does most of the work. When you create plans and add-ons, you also publish them, which triggers a static rebuild of the page. That rebuild is what lets the calculator load instantly, with no API call needed at runtime.
How Do You Render the Plans and Add-Ons on the Pricing Page?
Drop a Collection List into the pricing page and bind it to the Plans collection. Inside the Collection Item, use a Webflow Card structure with the plan name, base price, included seats, and a button that the user toggles to "select." The select state is what the calculator reads.
For the add-ons, drop a second Collection List bound to Add-Ons. Inside each item, add a custom data attribute on the wrapper, like data-applies-to, with the value bound to the Multi-Reference field's slug list. That attribute is what the JavaScript will check when it filters the visible add-ons based on the currently selected plan.
The trick that makes this readable is wrapping the price display in a fixed span with an ID, like #price-total, that the script can rewrite. Webflow's CMS will fill in the static price; the script only mutates the running total as the user interacts.
What Does the JavaScript Actually Need to Do?
The script does four things. It listens for clicks on plan select buttons. It reads the selected plan's base price and seats from data attributes Webflow has rendered into the DOM. It listens for changes to a seats input and to add-on toggles. It updates the total and the line-item breakdown in the DOM.
The whole thing runs without a fetch call to any API. Webflow has already baked the plan and add-on data into the page as data attributes, so the script reads from the DOM instead of going over the network. That keeps the calculator fast on a slow mobile connection in a hotel lobby in Pune or a café in Bengaluru, which is the actual constraint for my clients, not desktop performance.
I keep the script in a Webflow Page Settings custom code block on the pricing page only. That avoids loading it on every page and keeps the bundle out of the global script. If you want to see how I structure that, my piece on Webflow component-scoped interactions covers the page-scoped pattern in more detail.
How Do You Send the Calculator Result Into Your CRM?
The simplest path is to attach the final calculator state to a hidden field on the demo form just before submission. I use a Webflow form on the same page, with hidden inputs named "plan_selected," "seats," and "addons_selected." The script populates those inputs whenever the calculator state changes. When the buyer hits submit, the form already carries everything HubSpot needs.
From HubSpot, the routing is standard. The team uses HubSpot Workflows to score the lead based on the total monthly value attached, route it to a specific sales rep, and create a deal with a populated amount. The deal page in HubSpot now opens with the buyer's configuration already filled in, which saves the rep about ninety seconds on every call.
For the broader question of how Webflow forms can route into HubSpot, Salesforce, or Mailchimp without Zapier, the angle I cover in my piece on Webflow forms integration with HubSpot, Salesforce, and Mailchimp applies directly here.
How Do You Handle Currency and Localization for International Buyers?
For my Bengaluru-based clients selling globally, this matters. The cleanest way is to store base prices in USD inside the CMS, then convert at render time using a fixed conversion table the client controls. I add a second CMS collection called Currency Rates with a code (USD, INR, EUR), a multiplier, and a symbol. The script reads the visitor's locale and applies the right rate.
If you want the conversion to be dynamic against a live FX feed, you can call Open Exchange Rates or the Indian RBI reference rate once a day from a Cloudflare Worker, cache the result for 24 hours, and serve it as JSON. That keeps the calculator fast and the rates current within a day's tolerance, which is what almost every B2B SaaS pricing page actually needs.
What I avoid is converting on every page load through a live API call. That introduces latency you do not need on a pricing page, and it ties your conversion event to the uptime of a third-party currency service.
What About Accessibility and Conditional Logic Inside the Calculator?
Every interactive control needs a real label. The seats input is a number field with min and max attributes and an aria-describedby pointing at a help line. The plan select buttons are real buttons with aria-pressed states, not divs styled to look like buttons. The total at the bottom uses aria-live="polite" so screen readers announce price updates.
For conditional logic, like hiding an add-on that does not apply to the selected plan, I prefer to hide it with a CSS class the script toggles, rather than removing it from the DOM. That keeps the page structure stable and avoids confusing assistive tech. The class adds opacity reduction and pointer-events: none, which is enough to make the choice obvious without breaking the layout.
The accessibility piece is not optional in 2026, because the European Accessibility Act enforcement window opened on June 28, 2025. Pricing pages on B2B SaaS sites with EU revenue are inside scope.
What Does This Actually Replace and What Does It Cost?
It replaces Outgrow, Calconic, Involve.me, and any other third-party calculator embed. On the client site I rebuilt it for, the cost saving was 89 dollars a month for Outgrow, plus the cost of Zapier flows to push calculator submissions into HubSpot, which was another 29 dollars a month. The Webflow CMS solution is included in the existing site plan.
The one-time cost is engineering time. I spent six hours building the first version, including the JS, the styling, and the HubSpot integration. Maintenance since then has been roughly fifteen minutes a quarter, mostly to update pricing when the client launches a new plan. That is less than a single Outgrow invoice.
The intangible benefit is that the calculator now matches the brand exactly. There is no Outgrow watermark, no third-party domain in network requests, and no second-class styling pattern that ages out of the rest of the site.
How Should You Build This in Webflow This Week?
Set aside one afternoon. Create the Plans collection first, add three test plans, and bind a Collection List on the pricing page. Confirm everything renders. Then add the Add-Ons collection with a Multi-Reference to Plans, add three test add-ons, and bind a second Collection List. Style nothing yet. Just confirm the data renders correctly.
Then write the JavaScript in a Page Settings custom code block, kept small and tied to specific IDs you control. Test the calculator with three different plan and add-on combinations. Connect it to a form. Replace your third-party tool only after you have shipped the new calculator to a hidden URL and validated submissions land in your CRM properly.
If you want help wiring this together, especially the JavaScript and HubSpot piece for a calculator that fits your specific pricing model, I am happy to walk through it. Let's chat.
Get your website crafted professionally
Let's create a stunning website that drive great results for your business
Read more blogs
Get in Touch
This form help clarify important questions in advance.
Please be as precise as possible as it will save our time.