Problem
Recharts' default tick generation frequently produces non-round, hard-to-read axis values like $6,000, $28,000, $51,000 instead of clean values like $0, $25,000, $50,000, $75,000, $100,000.
This is a long-standing issue (#2140, #777, #1164) that affects virtually every chart that uses numeric axes with auto-generated ticks. The standard workaround is to compute explicit ticks arrays and pass them manually — but this defeats the purpose of a charting library handling it automatically.
Root cause
getTickValuesFixedDomain() in src/util/scale/getNiceTickValues.ts doesn't produce aesthetically clean tick values when the domain is explicitly set. The main getNiceTickValues() does better but still struggles with tickCount respect.
D3's d3-scale.nice() solves this well by snapping to 1/2/5 multiples of powers of 10. The current Recharts algorithm attempts something similar but the results are often not "nice" in practice.
Proposed fix
I'd be happy to submit a PR improving getTickValuesFixedDomain() to:
- Always start ticks at 0 when the domain includes 0
- Use clean 1/2/2.5/5/10 step multiples (similar to D3's approach)
- Respect
tickCount more accurately
- Maintain backward compatibility with existing tests
Here's a minimal version of the algorithm that works well in practice:
function niceTicks(dataMax: number, targetCount: number = 5): number[] {
if (dataMax <= 0) return [0];
const rawStep = dataMax / targetCount;
const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep)));
const normalized = rawStep / magnitude;
let niceStep: number;
if (normalized <= 1) niceStep = 1 * magnitude;
else if (normalized <= 2) niceStep = 2 * magnitude;
else if (normalized <= 2.5) niceStep = 2.5 * magnitude;
else if (normalized <= 5) niceStep = 5 * magnitude;
else niceStep = 10 * magnitude;
const niceMax = Math.ceil(dataMax / niceStep) * niceStep;
const ticks: number[] = [];
for (let v = 0; v <= niceMax; v += niceStep) {
ticks.push(Math.round(v * 1e10) / 1e10);
}
return ticks;
}
Before/After
Before (current Recharts default):
- X axis:
$6,000 $28,000 $51,000 $75,000 $100,000
- Y axis:
$6,500 $13,000 $19,500 $26,000
After (with nice ticks):
- X axis:
$0 $20,000 $40,000 $60,000 $80,000 $100,000
- Y axis:
$0 $5,000 $10,000 $15,000 $20,000 $25,000 $30,000
Context
Problem
Recharts' default tick generation frequently produces non-round, hard-to-read axis values like
$6,000,$28,000,$51,000instead of clean values like$0,$25,000,$50,000,$75,000,$100,000.This is a long-standing issue (#2140, #777, #1164) that affects virtually every chart that uses numeric axes with auto-generated ticks. The standard workaround is to compute explicit
ticksarrays and pass them manually — but this defeats the purpose of a charting library handling it automatically.Root cause
getTickValuesFixedDomain()insrc/util/scale/getNiceTickValues.tsdoesn't produce aesthetically clean tick values when the domain is explicitly set. The maingetNiceTickValues()does better but still struggles withtickCountrespect.D3's
d3-scale.nice()solves this well by snapping to 1/2/5 multiples of powers of 10. The current Recharts algorithm attempts something similar but the results are often not "nice" in practice.Proposed fix
I'd be happy to submit a PR improving
getTickValuesFixedDomain()to:tickCountmore accuratelyHere's a minimal version of the algorithm that works well in practice:
Before/After
Before (current Recharts default):
$6,000$28,000$51,000$75,000$100,000$6,500$13,000$19,500$26,000After (with nice ticks):
$0$20,000$40,000$60,000$80,000$100,000$0$5,000$10,000$15,000$20,000$25,000$30,000Context