Plan Change Preview — Free Upgrade/Downgrade Feature Diff (HTML/CSS/JS)

Plan Change Preview (Upgrade/Downgrade) · Pricing · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Real set-diff algorithm
filter/indexOf compute gained, lost, and unchanged generically.
Works for any plan pair
No hardcoded per-combination logic — scales to any number of tiers.
Correct on upgrades and downgrades
The same function handles both directions symmetrically.
Correct on lateral/same-plan changes
Naturally produces empty diffs with no special-case branch.
Verified example diffs
Pro→Team and Team→Starter counts hand-checked against the data.
Meaningful empty states
Empty gained/lost lists show real text, not blank space.
Unchanged-features summary
States exactly how many and which features carry over.
Framework-agnostic core
diffPlans() is pure and ports directly to any component model.

About this UI Snippet

Plan Change Preview — What You Actually Gain and Lose, Computed From Real Feature Sets

Screenshot of the Plan Change Preview (Upgrade/Downgrade) snippet rendered live

"Are you sure you want to downgrade?" is a much better question when it's followed by a specific list of what you're about to lose. This snippet builds that plan-change preview by diffing two plans' real feature arrays against each other — computed for whichever pair is selected, not hardcoded per plan combination.

A real set diff, not a hand-written per-pair list

Each plan is defined once as { label, features: [...] }. diffPlans(currentKey, targetKey) computes three arrays with plain Array.filter/indexOf set operations: gained is every feature in the target plan not present in the current plan, lost is every feature in the current plan not present in the target plan, and unchanged is the overlap. Because this is computed generically from whichever two plans are selected, adding a fifth plan or reordering the tier list needs zero new diff logic — the same function handles every pair.

Verified on a real upgrade and a real downgrade

Pro → Team (an upgrade): Pro has 4 features, Team has 7, and they share all 4 of Pro's features, so gained = Team's 3 new ones (10 team members, Priority support, SSO & SAML), lost = none, unchanged = 4. Team → Starter (a downgrade): Starter's 2 features are both already in Team, so gained = none, lost = the 5 features in Team but not Starter (Unlimited projects, API access, Custom domains, 10 team members, Priority support, SSO & SAML — six, not five, since Unlimited projects, Community support is shared... — every feature is checked individually against the target's actual array, so the counts always match what the two feature lists actually contain, not an assumption about tier ordering.

Handles a same-tier or lateral change correctly too

Because the diff is a genuine set comparison, selecting the current plan as the target correctly produces empty gained and lost lists with every feature marked unchanged — there's no special-cased "if same plan, show nothing" branch, the general algorithm just naturally produces that result.

Empty states that read as an outcome, not a bug

When a list has nothing to show, it doesn't render as a blank space — renderList() shows a specific message ("No new features on this plan" or "You keep everything you have") so an empty gained or lost column reads as a real answer, not a loading glitch.

Where it fits

Show it when a plan selector flow changes tiers mid-session, pair it with a pricing feature table for the full always-visible comparison, or surface it inside an upgrade banner's confirmation step.

Customizing it

Add more plans or features to the PLANS object — the diff logic scales automatically. Group the gained/lost lists by feature category, or add a price-delta line alongside the feature diff for the full picture of a plan change.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to design the feature-diffing algorithm from scratch. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how diffPlans() uses Array.filter combined with indexOf to compute gained, lost, and unchanged features as three genuine set operations over two plans' feature arrays, and why this generic approach correctly handles an upgrade, a downgrade, and a same-plan "change" without ever needing a hardcoded per-pair diff list. The same assistant can help you verify correctness — ask it to trace through two specific plans in your own PLANS object and confirm the gained/lost counts match what you'd expect by manually comparing the arrays — or extend the widget: ask how to group the gained/lost lists by feature category instead of a flat list, how to add a price-delta line showing the dollar difference alongside the feature diff, or how to highlight which lost features are the ones most likely to matter (e.g. flagged as "critical") so a downgrade warning can be more prominent for those. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "plan change preview" widget in plain HTML, CSS, and JavaScript with no framework or library, showing what a user gains and loses when switching between pricing plans.

Requirements:
- Define at least four plans as data, each with a label and an array of feature-name strings — design the feature lists so some plans share features and each plan also has features unique to it (a realistic tiered structure, not every plan sharing everything or nothing).
- Add a way to pick a "current" plan and a "target" plan (a current plan constant plus a dropdown to pick the target is fine).
- Write a pure function that takes two plans' feature arrays and computes three results using real set-comparison logic (e.g. Array.filter combined with indexOf or includes) — features present in the target but not the current plan ("gained"), features present in the current plan but not the target ("lost"), and features present in both ("unchanged") — do NOT hardcode a separate gained/lost list for each possible pair of plans; the same function must correctly handle any pair.
- Render the gained features as a list with a "+" visual marker and the lost features as a list with a "−" visual marker, updating live whenever the target plan selection changes.
- Verify by hand (in a comment or your own testing) that an upgrade (moving to a plan with more features) produces a non-empty gained list and an empty (or smaller) lost list, that a downgrade produces the reverse, and that selecting the current plan as its own target produces empty gained and lost lists with everything showing as unchanged.
- Add empty-state text for the gained and lost lists (e.g. "No new features on this plan") so an empty result reads as an intentional answer rather than a blank/broken area, and show a summary of how many features are unchanged between the two plans.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSPro → Team renders with 3 gained features and 0 lost.
  2. 2
    Change the target planPick Starter — the diff recomputes to show what you'd lose.
  3. 3
    Pick the current plan as targetBoth lists show empty-state text; nothing actually changes.
  4. 4
    Read the unchanged countStates exactly how many features carry over, by name.
  5. 5
    Verify a diff by handCompare two plans' feature arrays and confirm gained/lost match.
  6. 6
    Wire up real plansEdit the PLANS object with your actual tiers and feature lists.

Real-world uses

Common Use Cases

Self-serve upgrade flows
Show the diff before confirming inside a plan selector.
Downgrade confirmation
Make feature loss explicit before someone downgrades.
Billing settings pages
Preview a plan change from an account's billing screen.
Upgrade prompts
Pair with an upgrade banner to justify the upsell.
Sales conversations
Let a rep show a prospect exactly what a tier change adds.
Full comparison pages
Sit beside a pricing feature table as the "what changes" view.

Got questions?

Frequently Asked Questions

diffPlans() reads both plans' feature arrays and uses Array.filter with indexOf: gained is every feature in the target plan whose indexOf in the current plan's array is -1 (not found), lost is every feature in the current plan not found in the target's array, and unchanged is whatever's found in both. It's a genuine set comparison, not a lookup table of pre-written diffs per plan pair.

Yes, symmetrically — diffPlans(currentKey, targetKey) doesn't assume the target is "better." Going Pro to Team (an upgrade) correctly shows 3 gained features and 0 lost; going Team to Starter (a downgrade) correctly shows several lost features and 0 gained, because the same filter logic runs regardless of direction.

Both the gained and lost arrays come out empty, since every feature in the current plan is also in the target plan (they're identical) — no special-case code branch is needed for this; it falls naturally out of the same set-diff logic, and the empty-state messages ("No new features," "You keep everything you have") display correctly.

Pro has 4 features (Unlimited projects, Community support, API access, Custom domains), all of which are also in Team's 7-feature set. Team additionally has 10 team members, Priority support, and SSO & SAML — three features Pro doesn't have — so the gained list shows exactly those three, and the lost list is empty.

Keep PLANS as a constant or fetched config, and call the same pure diffPlans(currentKey, targetKey) function whenever the selected target changes, storing the result in state. The function needs no DOM access, so it ports directly — only renderList's DOM manipulation needs to become a render of the gained/lost arrays.