Dev.to WebDev 🛠 Dev 👁 0 📖 3 min read

Building Calculators That Show Their Own Formula (with Dynamic OG Images in Next.js)

Most ROI calculators end in a pitch. The number is a black box. I wanted the opposite: a calculator that shows the formula next to the result, so a reader can check the math, and a crawler can read a worked example witho

Most ROI calculators end in a pitch. The number is a black box. I wanted the opposite: a calculator that shows the formula next to the result, so a reader can check the math, and a crawler can read a worked example without running the form.

These are seven free planning calculators for service businesses. No signup. Nothing is saved unless someone emails themselves the estimate. They live at keencraft.tech/tools.

Pure functions for the math

Each formula lives in a small function, separate from the UI. The missed-call one is the whole model:

export function missedCallRevenue(input: MissedCallInput) {
  const missed = input.callsPerMonth * (input.missRate / 100);
  const bookings =
    missed * (input.bookableShare / 100) * (input.bookingRate / 100);
  const monthlyLeak = bookings * input.jobValue;
  const paybackMonths = monthlyLeak > 0 ? input.buildCost / monthlyLeak : null;
  return {
    missed,
    bookings,
    monthlyLeak,
    annualLeak: monthlyLeak * 12,
    paybackMonths,
  };
}

The dental starting example is 400 calls a month, 20% unanswered, 40% of those could have booked, a 50% booking rate when the call is answered, and $1,200 for one booking. That is 80 missed calls, 16 bookings, and $19,200 a month. The same function feeds the page, the starting example, and the share image.

Presets

Industry presets are plain objects. Picking one fills the fields, and every input stays editable.

const dentalPreset = {
  callsPerMonth: "400",
  missRate: "20",
  bookableShare: "40",
  bookingRate: "50",
  jobValue: "1200",
  buildCost: "8000",
};

Dental, HVAC and trades, med spa, legal intake, fitness, and parking sit beside it. A preset is a starting point, not a locked scenario.

A worked example in the HTML

The formula, the starting numbers, and the FAQs are rendered on the server. The form is a client component on top of that. A crawler that does not run JavaScript still sees the dental example and the formula.

Shareable result links

Changing an input writes only the fields that differ from the defaults into the query string. history.replaceState updates the address bar without a new history entry. Opening that URL reads the same keys back.

const params = new URLSearchParams();
for (const [key, value] of Object.entries(values)) {
  if (value !== defaults[key] && value !== "") params.set(key, value);
}
const query = params.toString();
const url = query ? `${path}?${query}` : path;
window.history.replaceState(null, "", url);

Copy result link builds the same URL from the current values.

Dynamic Open Graph images

Each calculator has an opengraph-image.tsx route. It calls sharePreview(slug), the same helper as the page, so the card shows the starting example. The agency margin card is labeled monthly gross profit on that example. Next.js renders the card with ImageResponse at 1200 by 630.

export const runtime = "nodejs";
export const size = { width: 1200, height: 630 };
export const contentType = "image/png";

export default async function Image({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const preview = sharePreview(slug);
  return new ImageResponse(
    (
      <div
        style={{
          display: "flex",
          backgroundColor: "#07111f",
          color: "#fff",
          width: "100%",
          height: "100%",
        }}
      >
        <div style={{ display: "flex", fontSize: 84 }}>{preview.value}</div>
      </div>
    ),
    { ...size },
  );
}

Every element in that tree sets display: "flex". The image renderer lays elements out with flex, and a node without it comes out blank.

FAQ and related links

Each tool page lists its formula, an FAQ, and links to the matching article. The agency margin page links to the white-label article. The missed-call page links to the dental article. Those links are in the HTML, not only inside the form.

The seven calculators

A small embed

The missed-call calculator also exists as a CodePen, with the same dental defaults.

The full version, with industry presets, is the missed-call revenue calculator.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.