The widget tax
Google "mortgage calculator widget" and you land on pricing pages: $9–$49/month, API keys, monthly view caps, and watermarks you pay extra to remove. If you run a real-estate blog and want one interactive payment calculator in a post, that's a weird amount of infrastructure for arithmetic.
I run Practical Web Tools, a site with ~500 calculators (finance, math, health, science, everyday) that all run 100% client-side. So I opened the whole catalog up as free embeds. This post is how the embed surface works and what it cost to make it not awful.
Two ways to embed
1. One-line script widgets. Nine flagship widgets — mortgage, loan/amortization, compound interest, BMI, calorie/TDEE, pregnancy due-date, percentage, countdown timer, word counter — each install as two lines:
<div id="pwt-mortgage-widget"></div> <script src="https://practicalwebtools.com/embed/mortgage-widget.js" defer></script> These are self-contained static scripts (a shared ~4KB engine renders them). Crucially, there is no iframe pointing at my servers: once the script is on your page, it runs entirely in your visitor's browser. If my site goes down, your embedded widget keeps working. Nothing phones home, because there is no home to phone.
2. Any calculator as an auto-sizing iframe. Every one of the ~500 calculators doubles as a widget at <calculator URL>?embed=1. Each calculator page shows its own copy-paste code; it looks like this:
<iframe src="https://practicalwebtools.com/finance/mortgage-calculator?embed=1" title="Mortgage Calculator" loading="lazy" style="display:block;width:100%;max-width:880px;height:720px;border:0" data-pwt-embed></iframe> <script src="https://practicalwebtools.com/embed/frame.js" async></script> <p style="margin:6px 0 0;font:13px/1.5 system-ui,sans-serif"> Mortgage Calculator by <a href="https://practicalwebtools.com/finance/mortgage-calculator">Practical Web Tools</a> </p> The embed URL renders the calculator with all site chrome stripped: no navigation, no ads, no cookie banner — just the tool. A theme=dark parameter pins a dark build when your site has a dark article layout.
How the auto-resize handshake works
The classic iframe-embed wart is the fixed height: too short and you get an inner scrollbar, too tall and there's a blank canyon under the widget. The fix is a tiny postMessage handshake.
Inside the frame, the calculator measures itself on load and on every DOM resize, then posts its height up:
// inside the framed calculator var send = function () { var h = Math.ceil(Math.max( document.documentElement.offsetHeight, document.body.offsetHeight )); parent.postMessage({ type: 'pwt-embed:resize', height: h }, '*'); }; new ResizeObserver(send).observe(document.body); On the host page, a framework-free, ES5-safe script (frame.js, ~1KB) listens for those messages and sizes the matching iframe:
window.addEventListener('message', function (e) { if (e.origin !== 'https://practicalwebtools.com') return; if (e.data.type !== 'pwt-embed:resize') return; // find the iframe whose contentWindow sent this, then: iframe.style.height = Math.min(Math.max(e.data.height, 160), 20000) + 'px'; }); If the host's script loads after the frame already reported (script order at the bottom of the page), the host pings back with {type:'pwt-embed:ping'} and the frame re-sends. Net effect: paste the snippet, get a widget that fits, zero configuration.
Things that bit me along the way
- Third-party frames deny storage. Browsers often throw on
localStorageaccess inside cross-origin iframes. Several calculators persist their inputs, so the embed bootstrap probes storage and shims in an in-memory fallback (getItem/setItem/removeItem/key/length) before any tool code runs. Without it, half the catalog would crash on frame load in Safari. - Height feedback loops. If the framed page has any viewport-sized element, applying the reported height changes the layout, which reports a new height, forever. The bootstrap detects same-step growth and stops reporting after five iterations rather than stretching the iframe into a scroll bomb.
- Ads and embeds don't mix. The site is ad-supported; embeds load zero ads and zero tracking scripts. A widget that renders ads on somebody else's article is a parasite, and site owners can tell.
- Theme defaults matter. "Follow the visitor's OS" sounds nice until a dark widget paints itself into a light article. Default is light, with an explicit opt-in for dark.
Who these are for
- Real estate / lending blogs — mortgage calculator, loan & amortization with a yearly schedule
- Fitness and nutrition sites — BMI and calorie/TDEE (Mifflin-St Jeor)
- Deal blogs and retail — percentage calculator (percent-off, % change, is-what-%)
- Parenting and pregnancy sites — due-date with trimester breakdown
- Personal finance writers — compound interest with contribution schedule
- Writing/SEO blogs — live word & character counter
Every calculator's "Embed this calculator" section has a theme picker and a live preview of exactly what your visitors will see, and the snippets work anywhere that accepts custom HTML: WordPress, Squarespace, Wix, Webflow, Ghost, GitHub Pages, documentation sites.
The catch
There is exactly one: a small Calculator by Practical Web Tools link with each widget. No API key, no signup, no view quota, no "upgrade for dark mode," no email wall. If that trade works for you, the full widget catalog is here:
→ Free Embeddable Calculator Widgets — the full catalog
And you can browse all ~500 calculators (every one embeddable from its own page) at practicalwebtools.com/calculators.
If you embed one, drop a comment with where — I keep a list and I'm taking requests for which calculator gets a dedicated one-line script next.
Tags: webdev, javascript, widgets, productivity

