Finance & Investing

Free Profit Margin Calculator Widget

Cost and sale price in — gross profit, margin, and markup out.

A compact calculator that turns a cost and a sale price into gross profit, profit margin %, and markup %, updating live as visitors type. Built for shops, freelancers, and anyone pricing products or services online. Paste one script tag where you want it and it renders itself — no signup, no dependencies.

Live demo

Embed this widget

Paste either snippet into your page — a Custom HTML block on WordPress, Shopify, or Squarespace works too. Both snippets update live as you customize, so what you copy is exactly what you configured.

Hosted (recommended)html
<script src="https://hayneslab.dev/widgets/profit-margin-calculator.js" async></script>
<!-- Free Profit Margin Calculator widget by haynes lab — https://hayneslab.dev/tools/widgets/profit-margin-calculator -->
Inline (self-contained)html
<!-- Free Profit Margin Calculator widget by haynes lab — https://hayneslab.dev/tools/widgets/profit-margin-calculator -->
<script>
/*!
 * haynes lab — Profit Margin Calculator widget (free)
 * Docs & embed code: https://hayneslab.dev/tools/widgets/profit-margin-calculator
 */
(function () {
  'use strict';

  var PREFIX = 'hlw-profit-margin-calculator';
  var STYLE_ID = 'hlw-profit-margin-calculator-styles';

  function findScript() {
    return document.currentScript ||
      document.querySelector('script[src*="widgets/profit-margin-calculator.js"]');
  }

  function injectStyles() {
    if (document.getElementById(STYLE_ID)) return;
    var css =
      '.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
      '.' + PREFIX + ' { position: relative; max-width: 460px; margin: 0 auto; padding: 28px 28px 36px;' +
      ' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
      ' font-size: 16px; line-height: 1.5; color: #1f2937; background: #ffffff;' +
      ' border: 1px solid #e5e7eb; border-radius: 12px; }' +
      '.' + PREFIX + '__title { margin: 0 0 20px; font-size: 18px; font-weight: 700; color: #111827; }' +
      '.' + PREFIX + '__fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }' +
      '.' + PREFIX + '__field { display: flex; flex-direction: column; gap: 6px; }' +
      '.' + PREFIX + '__label { font-size: 13px; font-weight: 600; color: #374151; }' +
      '.' + PREFIX + '__input { width: 100%; padding: 10px 12px; font-size: 15px; font-family: inherit;' +
      ' color: #111827; background: #ffffff; border: 1px solid #d1d5db; border-radius: 8px; }' +
      '.' + PREFIX + '__input:focus { outline: 2px solid var(--hlw-accent); outline-offset: 1px; border-color: var(--hlw-accent); }' +
      '.' + PREFIX + '__results { margin-top: 20px; padding-top: 18px; border-top: 1px solid #e5e7eb;' +
      ' display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }' +
      '.' + PREFIX + '__result { text-align: center; }' +
      '.' + PREFIX + '__value { display: block; font-size: 22px; font-weight: 700; color: #111827; }' +
      '.' + PREFIX + '__value--profit { color: var(--hlw-accent); }' +
      '.' + PREFIX + '__name { display: block; margin-top: 2px; font-size: 12px; color: #6b7280; }' +
      '.' + PREFIX + '__note { margin: 14px 0 0; font-size: 12px; color: #9ca3af; text-align: center; }' +
      '.' + PREFIX + '__credit { position: absolute; right: 10px; bottom: 6px; font-size: 11px;' +
      ' display: inline-flex; align-items: center; gap: 4px; color: #9ca3af; text-decoration: none; }' +
      '.' + PREFIX + '__credit svg { display: block; }' +
      '.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }' +
      '@media (max-width: 480px) { .' + PREFIX + ' { padding: 22px 18px 36px; }' +
      ' .' + PREFIX + '__fields { grid-template-columns: 1fr; }' +
      ' .' + PREFIX + '__value { font-size: 19px; } }';
    var style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = css;
    document.head.appendChild(style);
  }

  function el(tag, className, text) {
    var node = document.createElement(tag);
    if (className) node.className = className;
    if (text != null) node.textContent = text;
    return node;
  }

  function logoSvg() {
    var svgNS = 'http://www.w3.org/2000/svg';
    var svg = document.createElementNS(svgNS, 'svg');
    svg.setAttribute('viewBox', '0 0 32 32');
    svg.setAttribute('width', '13');
    svg.setAttribute('height', '13');
    svg.setAttribute('aria-hidden', 'true');
    svg.setAttribute('focusable', 'false');
    function circle(cx, cy, r, attrs) {
      var c = document.createElementNS(svgNS, 'circle');
      c.setAttribute('cx', cx);
      c.setAttribute('cy', cy);
      c.setAttribute('r', r);
      for (var key in attrs) c.setAttribute(key, attrs[key]);
      svg.appendChild(c);
    }
    circle(16, 16, 16, { fill: '#0f0a0a' });
    circle(16, 16, 12, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.3 });
    circle(16, 16, 9, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.5 });
    circle(16, 16, 6, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1.2, opacity: 0.9 });
    circle(16, 16, 3, { fill: '#06b6d4' });
    circle(24.5, 7.5, 2, { fill: '#f97316' });
    circle(7, 16, 1.8, { fill: '#f97316' });
    circle(20, 20, 1.5, { fill: '#f97316' });
    return svg;
  }

  function parseNum(raw, fallback) {
    var n = parseFloat(raw);
    return isNaN(n) ? fallback : n;
  }

  function build(script) {
    var title = script.getAttribute('data-hl-title') || 'Profit Margin Calculator';
    var currency = (script.getAttribute('data-hl-currency') || 'USD').toUpperCase();
    var accent = script.getAttribute('data-hl-accent') || '#f97316';
    var startCost = parseNum(script.getAttribute('data-hl-cost'), 40);
    var startPrice = parseNum(script.getAttribute('data-hl-price'), 65);

    var money;
    try {
      money = new Intl.NumberFormat(undefined, { style: 'currency', currency: currency });
    } catch (e) {
      console.warn('[haynes lab profit-margin-calculator] Unknown data-hl-currency "' + currency + '". Falling back to USD.', e);
      currency = 'USD';
      money = new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD' });
    }

    injectStyles();

    var root = el('div', PREFIX);
    root.style.setProperty('--hlw-accent', accent);

    root.appendChild(el('h3', PREFIX + '__title', title));

    var fields = el('div', PREFIX + '__fields');

    var costField = el('label', PREFIX + '__field ' + PREFIX + '__label');
    costField.appendChild(el('span', null, 'Cost (' + currency + ')'));
    var costInput = el('input', PREFIX + '__input');
    costInput.type = 'number';
    costInput.min = '0';
    costInput.step = 'any';
    costInput.value = String(startCost);
    costField.appendChild(costInput);

    var priceField = el('label', PREFIX + '__field ' + PREFIX + '__label');
    priceField.appendChild(el('span', null, 'Sale price (' + currency + ')'));
    var priceInput = el('input', PREFIX + '__input');
    priceInput.type = 'number';
    priceInput.min = '0';
    priceInput.step = 'any';
    priceInput.value = String(startPrice);
    priceField.appendChild(priceInput);

    fields.appendChild(costField);
    fields.appendChild(priceField);
    root.appendChild(fields);

    var results = el('div', PREFIX + '__results');
    results.setAttribute('aria-live', 'polite');

    function makeResult(name, valueClass) {
      var cell = el('div', PREFIX + '__result');
      var value = el('span', PREFIX + '__value' + (valueClass ? ' ' + valueClass : ''), '—');
      cell.appendChild(value);
      cell.appendChild(el('span', PREFIX + '__name', name));
      results.appendChild(cell);
      return value;
    }
    var profitOut = makeResult('Gross profit', PREFIX + '__value--profit');
    var marginOut = makeResult('Margin', null);
    var markupOut = makeResult('Markup', null);
    root.appendChild(results);

    var note = el('p', PREFIX + '__note');
    root.appendChild(note);

    function update() {
      var cost = parseNum(costInput.value, NaN);
      var price = parseNum(priceInput.value, NaN);
      if (isNaN(cost) || isNaN(price) || cost < 0 || price < 0) {
        profitOut.textContent = '—';
        marginOut.textContent = '—';
        markupOut.textContent = '—';
        note.textContent = 'Enter a cost and a sale price to see your numbers.';
        return;
      }
      var profit = price - cost;
      profitOut.textContent = money.format(profit);
      marginOut.textContent = price > 0 ? ((profit / price) * 100).toFixed(1) + '%' : '—';
      markupOut.textContent = cost > 0 ? ((profit / cost) * 100).toFixed(1) + '%' : '—';
      note.textContent = profit < 0
        ? 'Selling below cost — this deal loses money.'
        : 'Margin is profit as a share of the sale price; markup is profit as a share of cost.';
    }

    costInput.addEventListener('input', update);
    priceInput.addEventListener('input', update);
    update();

    var credit = el('a', PREFIX + '__credit');
    credit.appendChild(logoSvg());
    credit.appendChild(document.createTextNode('Powered by haynes lab'));
    credit.href = 'https://hayneslab.dev/tools/widgets/profit-margin-calculator';
    credit.target = '_blank';
    credit.rel = 'noopener';
    root.appendChild(credit);

    if (script.parentNode && script.parentNode.nodeName !== 'HEAD') {
      script.insertAdjacentElement('afterend', root);
    } else {
      document.body.appendChild(root);
    }
  }

  function init() {
    var script = findScript();
    if (!script) {
      console.warn('[haynes lab profit-margin-calculator] Could not locate its own script tag. Nothing rendered.');
      return;
    }
    build(script);
  }

  if (document.body) {
    init();
  } else {
    document.addEventListener('DOMContentLoaded', init);
  }
})();

</script>

Configuration

Every option is an attribute on the script tag — the same attributes the customizer above exposes. All attributes are optional unless marked required — defaults apply when omitted.

AttributeDefaultDescription
data-hl-title"Profit Margin Calculator"Heading shown at the top of the calculator card.
data-hl-cost"40"Pre-filled cost value visitors see before typing.
data-hl-price"65"Pre-filled sale price visitors see before typing.
data-hl-currency"USD"ISO 4217 currency code (e.g. USD, EUR, GBP) used to format gross profit and label the inputs.
data-hl-accent"#f97316"Hex color for the profit figure and input focus ring.

Frequently asked questions

How do I install the profit margin calculator?

Paste the embed snippet where you want the calculator to appear and it renders inline at that spot. Set data-hl-cost and data-hl-price to pre-fill starting numbers, and it recalculates live as visitors type.

How do I change the currency or colors?

Set data-hl-currency to any ISO 4217 code (EUR, GBP, CAD, ...) to reformat the profit figure and input labels, and data-hl-accent to any hex color for the profit highlight. The title, default cost, and default price are their own data-hl-* attributes.

Does it work on WordPress, Shopify, or Squarespace?

Yes — it is a single vanilla JavaScript file with no dependencies. Paste the script tag into an HTML block on WordPress, a Custom Liquid section on Shopify, or a Code Block on Squarespace and it just works.

More free widgets

Need a custom widget?

We build custom widgets, integrations, and whole products. Tell us what your site needs and we'll scope it.

Get in Touch