Skip to main content
In the Graphy SDK, your data and your spec are separate. Data is a plain table you pass to GraphProvider as the data prop; the spec references that table’s columns by key through its mapping. Keeping them apart means the same spec can be re-used across datasets, and the same data can be drawn several ways.

Basic structure

Data is a table with explicitly defined columns and rows:
The columns array defines the shape; rows holds the values.

Referencing columns from a spec

A spec never contains data — it names the columns it needs through its mapping. The variable strings you pass to mapping (or the createSpec shorthand) are column keys:
Because the binding is by key, the label on a column is purely presentational — it’s what shows in axes, legends and tooltips. If you omit it, the key is used.
Row keys must exactly match a column key. Values under keys with no matching column definition are ignored.

Value format detection

Every column gets one value format, which drives how a scale interprets it and how the renderer formats it. Inference runs per column, in two passes:
  1. The year pass. Across the first 5 non-empty rows, the first undeclared column whose non-empty cells are at least two whole numbers between 1800 and 2199 is read as a temporal year column. This is what keeps a 2021, 2022, 2023 column off a numeric axis. If any column declares valueFormat: { type: 'year' }, this pass is skipped.
  2. The first non-empty cell. Every other undeclared column takes its format from its first non-empty cell alone, tried in this order: number → date → weekly date range → percentage → currency → text.
Then every cell in the column is parsed under that one format. A cell that doesn’t fit becomes null — silently, with no warning. Order in the data therefore matters:
If a column looks empty on the chart, check its first non-empty cell first.

Declaring a format

The same cell can mean two things: 2010 is a headcount or a year, '1' a category or a number. When you know, set valueFormat on the column. A declared column skips inference and is parsed and formatted under that format:
Any format below is accepted; a temporal one can carry the dateFormat its cells use, such as { type: 'date', dateFormat: 'dd-MM-yyyy' }. A cell that does not fit becomes null, as under inference. Declare a numeric format for every count column whose values could look like years. Declaring a column named year as integer keeps that column numeric, but the year pass still checks undeclared siblings. For example, an undeclared population column containing 1950, 2000 would become dates. Declare both to keep both numeric:
Only a declaration with type: 'year' stops the year pass for the whole table; an integer declaration does not.

Numbers

Numbers and numeric strings are detected as quantitative values. Thousands separators, decimals and magnitude suffixes are supported:

Dates

Date-like strings are parsed automatically across a wide range of formats — ISO dates, named months and locale-specific orderings:
Short month names (Jan, Feb) are recognised alongside full names. Set data._metadata.parsingLocale to control whether ambiguous dates like 01/02/2024 are read day-first (en-GB, pt-PT, ar) or month-first (en-US). Weekly date ranges are also detected — values like 1 Feb – 7 Feb or 1 Feb 2024 – 7 Feb 2024 representing 7-day spans.

Dates without a year

A format that carries no year — month (Jan), day_month (14 Jan) or a weekly range without one — gets a synthetic year assigned by walking the column in row order, one sequence per color group. Each time the sequence wraps past December, the year advances. That’s why Jan … Dec, Jan, Feb reads as 14 consecutive months rather than folding back onto itself — and why reordering the rows moves the axis.

Percentages

Values with a % suffix are detected as percentages, and stored as fractions: '64.5%' becomes 0.645. The renderer multiplies by 100 again when it formats, so the axis reads back as you wrote it.
A bare numeric cell inside a percentage column passes through unscaled, so a 12 sitting among '64.5%' values renders as 1200%. Write '12%' to keep the column consistent.

Currencies

Currency-formatted strings are parsed with automatic symbol recognition. Supported symbols: $, €, £, ¥, ₹, ₱, ₩, ₪, ₫, ₽, ฿, ₦, ₺, zł, kr, Fr, R, P, R$, Rp, RM, د.إ, ﷼, Ch$, NT$, HK$, S$, A$, C$, NZ$, MX$.
Symbols can appear as prefix or suffix, and negative values are supported (-$100, $-100).

Text

Any value that doesn’t match the above is treated as text (categorical data).

How formats reach scales

The inferred format feeds the scale you declare for each aesthetic. Calling a position scale bare (scale.x()) lets the engine choose a scale type from the column’s format. For the position aesthetics — x, y, ySecondary:
  • A text column → a discrete (band) scale.
  • A numeric, percentage or currency column → a continuous scale.
  • A date column → a datetime scale.
The visual aesthetics read the same formats differently. On color, size, alpha and strokeWidth, a date column infers continuous — timestamps become plain numbers, since a colour ramp needs no date-aware ticks. On lineType, every format infers discrete. And an aesthetic bound to a constant ({ value: 5 }), left unmapped, or pointing at a column that isn’t in the data falls back to continuous. You can always override the inference with an explicit method — scale.x.continuous(), scale.x.discrete(), scale.x.datetime() — when you want to force a particular treatment. See the line chart guide for worked examples.

Empty and missing values

A cell counts as empty when it is null, undefined, an empty or whitespace-only string, or '-'. Empties become null in the parsed column, and two rules follow from that:
  • A row whose every cell is empty is dropped.
  • A column with no non-empty cell at all is dropped, so nothing can map to it.
How a geom treats the resulting gaps is geom-specific — for lines, the missingValues param chooses whether to break the path, bridge it, or treat the gap as zero.

Schema

Column[]
required
Array of column definitions. Each column defines the structure and metadata for a data field.
string
required
Unique, stable identifier for the column. Must match the keys used in rows objects, and is what a spec’s mapping references.
string
Human-readable label shown in the UI (axis labels, legends, tooltips). Defaults to the key if not provided.
ExplicitValueFormat
How to read the column’s cells when you know better than inference, for example { type: 'integer' }, { type: 'text' } or { type: 'currency', iso: 'usd' }. A declared column skips inference; see Declaring a format.
Row[]
required
Array of data rows. Each row is an object with keys matching the column keys. Values can be a string, number, Date or null.
object
Host state carried alongside the table. The engine reads parsingLocale; the remaining keys are set by the Graphy editor and are not part of the SDK’s authoring surface.
There are two parsingLocale settings, and they do different jobs. data._metadata.parsingLocale (default 'en-GB') decides how input cells are parsed. config({ parsingLocale }) (default 'en-US') decides how values are formatted for display, and is what formattingLocale on GraphProvider overrides. Setting the config one does not change how a date string is read.