DEV Community

Cover image for How I built 38 free marketplace fee calculators as a fully static Next.js site
Hussnain Ali
Hussnain Ali

Posted on

How I built 38 free marketplace fee calculators as a fully static Next.js site

Every day, someone in a seller forum asks the same question: "How much will eBay actually take from this sale?" The answers are usually half right. Fees depend on the category, the country, the shipping, the sales tax, and sometimes on the price itself.

So I built Selling Fee Tools: 38 free fee calculators for 14 marketplaces and payment providers (eBay, Etsy, Amazon, Shopify, TikTok Shop, Walmart, PayPal, Stripe, Poshmark, Depop, Mercari, Whatnot, Vinted and Facebook Marketplace), with separate versions for the US, UK, Canada and Australia. Plus a few data tools for store owners: WooCommerce ⇄ Shopify CSV converters, a Shopify CSV validator and a Google Merchant Center feed validator.

Here is how it's built, and the parts that were harder than they looked.

The constraint: no backend at all

I set one rule from the start: everything runs in the browser. No uploads, no API routes, no database.

  • Sellers paste product exports with supplier costs and margins. Not uploading them is the easiest privacy policy there is.
  • A static site is fast and costs almost nothing to host.
  • Every page is plain HTML that search engines can read.

The stack:

  • Next.js 15 (App Router) with output: 'export': the build produces a folder of static files.
  • TypeScript everywhere, Tailwind CSS v4 with all colours as CSS variables.
  • PapaParse for CSV, switching to a web worker for files over 5 MB so the page doesn't freeze.
  • Canvas API plus fflate to resize marketplace images and zip them in the browser.
  • Node's built-in test runner (node --test) running the .ts files directly: 302 tests, no Jest.

Fees are data, not code

The biggest decision was to keep every rate out of the code. Each platform has a JSON file:

{
  "us": {
    "sourceUrl": "https://poshmark.com/fee_policy",
    "verifiedOn": "2026-11-02",
    "threshold": 15,
    "flatFee": 2.95,
    "pct": 20
  }
}
Enter fullscreen mode Exit fullscreen mode

Two fields are mandatory on every object: sourceUrl (the platform's own fee page) and verifiedOn. A loader validates them at build time, so a missing source or a malformed date fails next build. Each tool page prints the date in a trust strip, and once it's more than 90 days old, a "Rates may have changed" note appears.

When a platform changes its fees, I edit one JSON file, update the date, add a changelog entry and run the tests.

The engines are pure functions

Each calculator is a pure TypeScript function: input in, line-by-line fees out. No React inside. That makes them easy to test against the platforms' own worked examples. eBay publishes a $400 example that should come to $58.41, and the test checks exactly that.

The details that matter:

Rounding. Platforms round each fee to the cent separately, then add them up. Rounding the total instead is off by a cent surprisingly often, and sellers do compare against their invoices. A tiny helper does it everywhere:

export const cents = (x: number) => Math.round((x + Number.EPSILON) * 100) / 100
Enter fullscreen mode Exit fullscreen mode

What the fee is charged on. eBay charges its percentage on the total the buyer pays, including sales tax: a $50 sale in most categories pays $7.20, but $7.74 with 8% sales tax. Etsy charges its transaction fee on the item and shipping, and its payment processing on the total including tax. Facebook Marketplace charges 10% on item, shipping and tax. Every engine has its own base, written down in a comment at the top of the file.

Banded rates. Some fees apply one rate up to a threshold and another above it (eBay: 13.6% up to $7,500, then 2.35%). Others charge the whole amount at the rate of the band it falls into. One shared Rate type handles both.

"What should I list it at?" is a search problem

Most sellers don't want the fee. They want the price that leaves them a target profit. You'd think you could just invert the formula, but fixed fees, minimums and bands make that messy.

Since the profit for a given price is cheap to compute, the calculators binary-search over cents: find the lowest price whose profit reaches the target.

There is one case where that breaks. Poshmark charges a flat $2.95 under $15 and 20% from $15. So:

  • $14.99 earns $12.04
  • $15.00 earns $12.00

Earnings drop at the threshold, so the function isn't monotonic and a plain binary search can give the wrong answer. The Poshmark engine tries the flat-fee band first, then searches the 20% band from the threshold up. The page also warns sellers that every price from $15.00 to $15.05 earns less than $14.99 does.

Converting product CSVs in the browser

The WooCommerce → Shopify converter was the trickiest tool:

  • WooCommerce exports a variable product as a parent row plus one row per variation. Shopify wants one Handle shared by all variant rows, with attributes as Option1–Option3.
  • A WooCommerce variation can say "Any Size". Shopify has no "any", so it becomes one variant for every size on the parent.
  • Excel often re-saves CSVs as Windows-1252, so the converter detects the encoding before parsing.
  • Anything that can't move, such as grouped products or more than three attributes, is listed with its row number instead of being dropped silently.

All of it happens in the tab. The file never leaves the device.

SEO for a tools site

Each tool targets the search people actually type: "{platform} fee calculator {country}". So eBay has separate pages for the US, UK, Canada and Australia, each with its own rates, copy and FAQ.

  • Every page is static HTML with WebApplication, FAQPage and BreadcrumbList JSON-LD.
  • No trailing slashes anywhere: links, canonicals and the sitemap all match.
  • One environment variable, NEXT_PUBLIC_SITE_URL, decides whether pages are indexable, so the preview deployment never competed with the real domain.
  • 66 guides answer the questions sellers search for, and each one links to at least two tools.

Cookie consent without a third-party script

The only cookies come from Google Analytics. A small inline script sets Google Consent Mode v2 defaults in <head> before GA loads: analytics storage is denied in the UK, the EEA and Switzerland until the visitor clicks Accept, and granted elsewhere unless they click Reject. The banner itself is about 40 lines of React, fixed to the bottom of the screen so it never shifts the layout.

What I'd tell myself at the start

  1. Put the source next to every number. It slows you down at first and saves you every time a platform changes its fees.
  2. Test against the platform's own examples. If eBay says $58.41, the test says $58.41.
  3. Static is enough for a surprising amount of "app".

The site is live at sellingfeetools.com. It's free, with no sign-up. If you sell online, I'd love to hear which platform I should add next.

Top comments (0)