<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hussnain Ali</title>
    <description>The latest articles on DEV Community by Hussnain Ali (@hussnain_ali_3235e58b8ecf).</description>
    <link>https://dev.to/hussnain_ali_3235e58b8ecf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4224005%2Fd17cde68-66ff-4ddd-b47c-02f5ffa4aa25.jpg</url>
      <title>DEV Community: Hussnain Ali</title>
      <link>https://dev.to/hussnain_ali_3235e58b8ecf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hussnain_ali_3235e58b8ecf"/>
    <language>en</language>
    <item>
      <title>How I built 38 free marketplace fee calculators as a fully static Next.js site</title>
      <dc:creator>Hussnain Ali</dc:creator>
      <pubDate>Tue, 03 Nov 2026 10:23:49 +0000</pubDate>
      <link>https://dev.to/hussnain_ali_3235e58b8ecf/how-i-built-38-free-marketplace-fee-calculators-as-a-fully-static-nextjs-site-203k</link>
      <guid>https://dev.to/hussnain_ali_3235e58b8ecf/how-i-built-38-free-marketplace-fee-calculators-as-a-fully-static-nextjs-site-203k</guid>
      <description>&lt;p&gt;Every day, someone in a seller forum asks the same question: &lt;em&gt;"How much will eBay actually take from this sale?"&lt;/em&gt; The answers are usually half right. Fees depend on the category, the country, the shipping, the sales tax, and sometimes on the price itself.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;a href="https://sellingfeetools.com" rel="noopener noreferrer"&gt;Selling Fee Tools&lt;/a&gt;&lt;/strong&gt;: 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.&lt;/p&gt;

&lt;p&gt;Here is how it's built, and the parts that were harder than they looked.&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraint: no backend at all
&lt;/h2&gt;

&lt;p&gt;I set one rule from the start: &lt;strong&gt;everything runs in the browser.&lt;/strong&gt; No uploads, no API routes, no database.&lt;/p&gt;

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

&lt;p&gt;The stack:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Fees are data, not code
&lt;/h2&gt;

&lt;p&gt;The biggest decision was to keep every rate out of the code. Each platform has a JSON file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"us"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://poshmark.com/fee_policy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"verifiedOn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-11-02"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"flatFee"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two fields are mandatory on every object: &lt;code&gt;sourceUrl&lt;/code&gt; (the platform's own fee page) and &lt;code&gt;verifiedOn&lt;/code&gt;. A loader validates them at build time, so a missing source or a malformed date &lt;strong&gt;fails &lt;code&gt;next build&lt;/code&gt;&lt;/strong&gt;. 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.&lt;/p&gt;

&lt;p&gt;When a platform changes its fees, I edit one JSON file, update the date, add a changelog entry and run the tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  The engines are pure functions
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The details that matter:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rounding.&lt;/strong&gt; 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EPSILON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What the fee is charged on.&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Banded rates.&lt;/strong&gt; 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 &lt;code&gt;Rate&lt;/code&gt; type handles both.&lt;/p&gt;

&lt;h2&gt;
  
  
  "What should I list it at?" is a search problem
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;There is one case where that breaks. Poshmark charges a flat $2.95 under $15 and 20% from $15. So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$14.99 earns &lt;strong&gt;$12.04&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;$15.00 earns &lt;strong&gt;$12.00&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Earnings &lt;em&gt;drop&lt;/em&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting product CSVs in the browser
&lt;/h2&gt;

&lt;p&gt;The WooCommerce → Shopify converter was the trickiest tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;li&gt;A WooCommerce variation can say "Any Size". Shopify has no "any", so it becomes one variant for every size on the parent.&lt;/li&gt;
&lt;li&gt;Excel often re-saves CSVs as Windows-1252, so the converter detects the encoding before parsing.&lt;/li&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of it happens in the tab. The file never leaves the device.&lt;/p&gt;

&lt;h2&gt;
  
  
  SEO for a tools site
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Cookie consent without a third-party script
&lt;/h2&gt;

&lt;p&gt;The only cookies come from Google Analytics. A small inline script sets Google Consent Mode v2 defaults in &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell myself at the start
&lt;/h2&gt;

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

&lt;p&gt;The site is live at &lt;strong&gt;&lt;a href="https://sellingfeetools.com" rel="noopener noreferrer"&gt;sellingfeetools.com&lt;/a&gt;&lt;/strong&gt;. It's free, with no sign-up. If you sell online, I'd love to hear which platform I should add next.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>ecommerce</category>
    </item>
  </channel>
</rss>
