mrkeyoor.com_
Sat 08 Aug 21:01 UTC
npmWeb Frontendupdated 08 Aug 2026

@visx/scale

@visx/scale is a typed configuration layer over D3 scale functions. It maps data domains to visual ranges such as pixels, radii, colors, or categories and exposes factories for linear, band, point, time, UTC, log, power, square-root, symmetric-log, radial, ordinal, quantile, quantize, and threshold scales. It has no React peer and does not render anything; React charts call the returned functions while drawing axes, shapes, labels, and interactions with other visx packages or their own components.

Verdict

Use @visx/scale when visx is already your chart foundation or its typed config objects simplify a shared visualization layer. Use `d3-scale` directly when you only need scale math, and choose a higher-level chart library when axes, marks, legends, interaction, and accessibility should arrive together.

API stability4/5The package deliberately mirrors mature D3 scale concepts and keeps stable factory names for its major scale families. Version 4 changed package entry points and requires coordinated visx upgrades, but the migration guide says existing component APIs remain and root scale imports continue. Runtime-config helpers add a typed layer without changing callable D3 semantics.
Docs4/5The package page explains domains, ranges, SVG y reversal, each major scale family, log-domain restrictions, and color-scale composition, while the main visx site supplies gallery examples and a detailed v4 migration guide. Much behavioral detail is delegated to linked D3 docs, and helpers such as `createScale`, `updateScale`, `getTicks`, and inference receive little narrative coverage.
Maintenance5/5Version 4.0.0 was published in June 2026 and the repository was pushed later that month. The stable v4 release includes modernized ESM output, explicit entry points, coordinated package versions, React 18 and 19 guidance for related packages, and migration notes for failed alphas. The repository is active and not archived.
Ecosystem5/5@visx/scale recorded 4,383,173 downloads in the measured week and the visx repository has 20,999 stars. It interoperates with the full visx set of axes, shapes, groups, legends, responsive tools, tooltips, themes, and XY charts while retaining D3's familiar scale methods and compatibility with D3 color schemes.

Use it if

  • You build custom visx charts and want scale factories whose object configs fit the rest of the visx API
  • You need TypeScript inference across continuous, discrete, temporal, radial, and threshold scale types
  • You want D3 scale behavior without importing from D3 directly throughout a visx-based design system
  • You need helpers that create scales from a runtime `type`, copy and update an existing scale, infer its type, or get ticks generically
Skip it if

Setup reality

Install with `npm install @visx/scale`. The package has one runtime dependency, `@visx/vendor` 4.0.0, and no React, React DOM, native, credential, or config requirement. It ships CommonJS in `lib`, ESM in `esm`, TypeScript declarations, and marks itself side-effect-free. Import from the package root, not internal `lib` paths; visx v4 formalized package entry points and its migration guide tells users to remove deep imports. Although the wider visx v4 React packages require React 18 or 19, this root scale package is calculation-only and declares no peer dependency. Its returned values are D3-style callable objects with methods such as `domain()`, `range()`, `ticks()`, `invert()`, `bandwidth()`, or `copy()` depending on scale type. These scales are mutable: calling a method changes that instance. The visx `updateScale()` helper avoids that by copying before applying a new config. Continuous scales extrapolate outside the domain unless `clamp: true`; `nice: true` expands domain endpoints; `round: true` changes output precision; and a reversed SVG y-axis normally needs a range like `[height, 0]`. A band lookup can return `undefined` for an unknown category, so do not pass its result straight to SVG arithmetic without a check. Ordinal scales need an explicit `unknown` policy if accidental new domain values must not be learned. Log domains must be entirely positive or entirely negative and cannot touch or cross zero; use `scaleSymlog` when zero or signs must coexist. `scaleTime` uses local calendar time while `scaleUtc` avoids local-zone tick differences. Quantize divides a continuous interval evenly, quantile divides observed samples by distribution, and threshold uses caller-supplied boundaries; choosing the wrong one silently produces a misleading legend. `nice`, `zero`, `reverse`, padding, and interpolation are applied in a defined operator order, so inspect the final `domain()` and `range()` in tests. Scales do not memoize themselves for React; recreate them with `useMemo` when referential identity matters to children or effects.

Patterns

Map a numeric domain to pixelsmap-linear-values

import { scaleLinear } from '@visx/scale';

const yScale = scaleLinear({
  domain: [0, Math.max(...values)],
  range: [chartHeight, 0],
  nice: true,
  clamp: true,
});

const y = yScale(value);

SVG y coordinates increase downward, so the range is reversed. `nice` may expand the domain beyond the supplied maximum.

Position categorical barslayout-band-chart

import { scaleBand } from '@visx/scale';

const xScale = scaleBand({
  domain: data.map((row) => row.category),
  range: [0, innerWidth],
  padding: 0.2,
  round: true,
});

const x = xScale(row.category);
const width = xScale.bandwidth();

A value outside the domain maps to `undefined`. Guard it before assigning an SVG coordinate.

Center categories without bandwidthposition-categorical-points

import { scalePoint } from '@visx/scale';

const xScale = scalePoint({
  domain: ['Q1', 'Q2', 'Q3', 'Q4'],
  range: [0, innerWidth],
  padding: 0.5,
});

Point scales have no band width; use them for dots and line vertices, not bars that need a rectangle width.

Create a local-time axis scalescale-local-time

import { scaleTime } from '@visx/scale';

const xScale = scaleTime({
  domain: [new Date(2026, 0, 1), new Date(2026, 11, 31)],
  range: [0, innerWidth],
  nice: true,
});

const ticks = xScale.ticks(6);

Local time can produce daylight-saving and locale-dependent boundaries. Use `scaleUtc` for consistent server and client ticks.

Keep temporal ticks in UTCscale-utc-time

import { scaleUtc } from '@visx/scale';

const xScale = scaleUtc({
  domain: [new Date('2026-01-01T00:00:00Z'), new Date('2026-02-01T00:00:00Z')],
  range: [0, innerWidth],
});

UTC scaling avoids local daylight-saving boundaries, but tick labels still need an explicit UTC formatter.

Use a logarithmic scalescale-orders-of-magnitude

import { scaleLog } from '@visx/scale';

const sizeScale = scaleLog({
  domain: [1, 1_000_000],
  range: [2, 40],
  base: 10,
  clamp: true,
});

A log domain must be strictly positive or strictly negative. It cannot include zero or cross between signs.

Use symmetric log around zeroscale-signed-values

import { scaleSymlog } from '@visx/scale';

const xScale = scaleSymlog({
  domain: [-10_000, 10_000],
  range: [-200, 200],
  constant: 10,
});

Symlog accepts zero and both signs. The `constant` controls the size of the near-zero linear region.

Map categories to fixed colorsassign-category-colors

import { scaleOrdinal } from '@visx/scale';

const colorScale = scaleOrdinal({
  domain: ['success', 'warning', 'error'],
  range: ['#16803c', '#a15c00', '#b42318'],
  unknown: '#777777',
});

Set `unknown` when unexpected categories must not extend the ordinal domain implicitly or recycle a palette color.

Quantize a numeric intervalbin-even-intervals

import { scaleQuantize } from '@visx/scale';

const severity = scaleQuantize({
  domain: [0, 100],
  range: ['low', 'medium', 'high'],
});

console.log(severity(72)); // high

Quantize makes equal-width numeric bins. It does not balance the number of observations in each bucket.

Create quantile buckets from samplesbin-by-distribution

import { scaleQuantile } from '@visx/scale';

const quartile = scaleQuantile({
  domain: observations,
  range: ['q1', 'q2', 'q3', 'q4'],
});

const boundaries = quartile.quantiles();

The domain is the sample distribution, not just `[min, max]`; outliers and repeated values affect the calculated boundaries.

Map caller-defined breakpointsapply-explicit-thresholds

import { scaleThreshold } from '@visx/scale';

const risk = scaleThreshold({
  domain: [20, 50, 80],
  range: ['minimal', 'low', 'high', 'critical'],
});

A threshold scale needs one more range value than domain boundary. Document which side of each boundary owns equality.

Create a typed scale from configurationcreate-runtime-scale

import { createScale, getTicks, inferScaleType, updateScale } from '@visx/scale';

const scale = createScale({
  type: 'linear',
  domain: [0, 10],
  range: [0, 400],
});

const resized = updateScale(scale, { range: [0, 800] });
console.log(inferScaleType(resized), getTicks(resized, 5));

`updateScale` copies the scale before applying config, so `scale` retains its original range. Omitting `type` from `createScale` defaults to linear.

Alternatives

PackageRegistryPick it when
d3-scalenpmYou want the upstream scale API directly, broader D3 documentation, and no visx-specific config wrapper
@nivo/scalesnpmYou already use Nivo and want scales aligned with its higher-level chart components and configuration
chroma-jsnpmYour main problem is color interpolation, palettes, contrast, and color-space conversion rather than positional scales