/writing
Building Interactive Pricing Pages with Next.js
· 3 min read
A pricing page has to behave like an app and load like a landing page. People expect sliders, calculators and live recalculation. They also expect it to appear instantly, rank well, and ship almost no JavaScript. Lean too far either way and you lose the other side.
I built the pricing system for a SaaS product site in Next.js: plan selection, live cost calculation, feature comparison tables, and layouts that held up across dozens of plan configurations.
The challenge
The product had several pricing tiers across a few product lines. Each product had its own plan structure, its own comparison table, and its own volume pricing. Some plans billed annually, some monthly. Some had usage calculators driven by sliders. All of it came from a CMS.
I wanted one pricing system that could render any product’s page without copying code.
Dynamic components
I built a set of components that rendered from CMS data: plan cards, comparison tables, volume discount sections, FAQ accordions, banners. Each one was a block the CMS could reorder, show or hide per product.
The range slider was the most interesting piece. You drag to set your expected usage and the price updates as you go. It needed custom styling, keyboard support, and smooth movement between values. A native range input looks different in every browser, so I styled it with CSS custom properties and pseudo-elements.
Plan selection logic
Every product had a different plan structure, and usage options changed the price. The plan button had to know the current plan, the selected plan, whether the change was an upgrade or a downgrade, and whether the visitor was signed in.
I pulled that into a few utility functions. One helper returned the button’s attributes and a separate handler acted on the click. The components stayed simple and the logic stayed testable.
Comparison tables
The tables had to work on a phone without scrolling sideways. I used a grid that collapsed into a stacked layout on small screens, repeating the plan name on each feature row so you never lost track of which column you were reading.
Tooltips are CSS only. A JavaScript tooltip library is a lot of bundle for a marketing page.
One system, four products
“One pricing system for any product” was a goal when I wrote the first version. It is now how pricing ships for four of them: chat, video, activity feeds and moderation.
What made it survive was keeping per-product behavior in slices over a shared base. A product that prices unusually adds a slice instead of forking the system, so the moderation cards and the feeds overage tables sit next to each other without either knowing the other exists. The video usage calculator eventually came out of the page altogether and now embeds on its own.
I am also not the only one shipping into it. Seven other people have committed to the pricing code since, and it has kept absorbing new products for two years after the first version went out. That is the part I would call the actual result. Not the sliders, but the fact that adding a product line stopped being a conversation about the pricing page.
What I learned
Pricing pages are marketing pages that behave like applications. They need a dashboard’s interactivity and a landing page’s load time. The only way to get both is to decide, piece by piece, which side each one belongs to.
More writing
Encoding Pricing Rules Without Forking the UI
How I moved pricing math out of components and into the plan definition so a marketing site could absorb new products and billing models without UI changes.
· 3 min read
Hybrid Next.js: Static Export and SSR from One Codebase
How I shipped a Next.js marketing site that builds as a static export for one host and as a server-rendered app on Vercel, without forking the codebase.
· 3 min read
Deploying Swagger UI for Bacen's Pix API
A pull request to the Brazilian central bank that was closed without ever being merged, and shipped anyway. Rendering an OpenAPI spec as docs, from CI.
· 2 min read