When building marketing tools or internal ad-ops dashboards, calculating Pay-Per-Click (PPC) metrics in real time is a common requirement. A robust CPC (Cost Per Click) Calculator needs to do two things:
- Solve for missing metrics dynamically (
Ad Spend,Total Clicks, orCPC). - Help developers and ad ops team members determine their Max CPC Bid based on Target CPA and conversion rates.
In this tutorial, we will build a production-ready CPC & Max Bid calculator using React, TypeScript, and clean mathematical logic.
1. Understanding the CPC Core Formulas
Before diving into the React implementation, let's look at the underlying math:
Basic Cost Per Click (CPC):
CPC = Total Ad Spend \ Total ClicksMax CPC Bid Formula:
Max CPC = Target CPA * Conversion Rate %
2. Setting Up the State Logic in React
We want our calculator to update dynamically when any two input parameters are present. Here is a TypeScript hook that encapsulates the calculation logic safely without state mutation bugs:
import { useState, useMemo } from 'react';
interface CPCInput {
adSpend: number | '';
clicks: number | '';
cpc: number | '';
targetCPA: number | '';
conversionRate: number | ''; // input as percentage (e.g., 2.5)
}
export const useCPCCalculator = () => {
const [inputs, setInputs] = useState<CPCInput>({
adSpend: '',
clicks: '',
cpc: '',
targetCPA: '',
conversionRate: '',
});
const handleInputChange = (key: keyof CPCInput, value: string) => {
const numericValue = value === '' ? '' : parseFloat(value);
setInputs((prev) => ({ ...prev, [key]: numericValue }));
};
const calculatedResults = useMemo(() => {
const spend = typeof inputs.adSpend === 'number' ? inputs.adSpend : 0;
const clicks = typeof inputs.clicks === 'number' ? inputs.clicks : 0;
const cpc = typeof inputs.cpc === 'number' ? inputs.cpc : 0;
let derivedCPC = cpc;
let derivedSpend = spend;
let derivedClicks = clicks;
// Dynamically calculate missing parameter
if (spend > 0 && clicks > 0) {
derivedCPC = parseFloat((spend / clicks).toFixed(2));
} else if (spend > 0 && cpc > 0) {
derivedClicks = Math.round(spend / cpc);
} else if (clicks > 0 && cpc > 0) {
derivedSpend = parseFloat((clicks * cpc).toFixed(2));
}
// Max CPC calculation based on Target CPA
let maxBid = 0;
if (typeof inputs.targetCPA === 'number' && typeof inputs.conversionRate === 'number') {
maxBid = parseFloat((inputs.targetCPA * (inputs.conversionRate / 100)).toFixed(2));
}
return {
cpc: derivedCPC,
spend: derivedSpend,
clicks: derivedClicks,
maxBid,
};
}, [inputs]);
return { inputs, handleInputChange, calculatedResults };
};
3. Building the Component Interface
Now, let's connect this custom hook into a React form component:
import React from 'react';
import { useCPCCalculator } from './useCPCCalculator';
export const CPCCalculatorWidget: React.FC = () => {
const { inputs, handleInputChange, calculatedResults } = useCPCCalculator();
return (
<div className="p-6 max-w-lg mx-auto bg-white rounded-xl shadow-md space-y-4">
<h2 className="text-xl font-bold">Ad Budget & CPC Solver</h2>
<div className="space-y-3">
<div>
<label className="block text-sm font-medium">Total Ad Spend ($)</label>
<input
type="number"
value={inputs.adSpend}
onChange={(e) => handleInputChange('adSpend', e.target.value)}
className="w-full p-2 border rounded"
placeholder="e.g. 1000"
/>
</div>
<div>
<label className="block text-sm font-medium">Total Clicks</label>
<input
type="number"
value={inputs.clicks}
onChange={(e) => handleInputChange('clicks', e.target.value)}
className="w-full p-2 border rounded"
placeholder="e.g. 2500"
/>
</div>
<div>
<label className="block text-sm font-medium">Target CPA ($)</label>
<input
type="number"
value={inputs.targetCPA}
onChange={(e) => handleInputChange('targetCPA', e.target.value)}
className="w-full p-2 border rounded"
placeholder="e.g. 50"
/>
</div>
<div>
<label className="block text-sm font-medium">Conversion Rate (%)</label>
<input
type="number"
value={inputs.conversionRate}
onChange={(e) => handleInputChange('conversionRate', e.target.value)}
className="w-full p-2 border rounded"
placeholder="e.g. 3.5"
/>
</div>
</div>
<div className="mt-4 p-4 bg-gray-50 rounded-lg">
<h3 className="font-semibold text-lg">Results</h3>
<p>Calculated CPC: <strong>${calculatedResults.cpc}</strong></p>
<p>Optimal Max CPC Bid: <strong>${calculatedResults.maxBid}</strong></p>
</div>
</div>
);
};
Conclusion & Live Reference
Building client-side marketing utility calculators in Next.js is a quick way to deliver interactive user experiences without hitting backend APIs.
If you want to test a full-featured live tool including industry benchmark breakdowns, check out the live CPC Calculator on GM Calculator.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.