> ## Documentation Index
> Fetch the complete documentation index at: https://docs.graphy.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# How a chart is built

Every chart in the Graphy SDK is a **spec** — a plain, immutable object you build by folding parts together.

## The builder pipeline

You start a spec with `createSpec` and fold parts onto it with `pipe`. Each part is a small tagged object produced by a builder (`geom.*`, `scale.*`, `coord.*`, `config`, …):

```tsx theme={null}
import { createSpec, pipe, geom, scale } from '@graphysdk/viz-engine';

const spec = pipe(
  createSpec({ x: 'month', y: 'revenue' }),
  geom.line(),
  scale.x(),
  scale.y()
);
```

`pipe(spec, ...parts)` returns a **new** spec with each part folded in, left to right — it never mutates. Parts accumulate by kind: every `geom.*` adds a layer, every `scale.*` adds a scale, `config` deep-merges, `coord` and `mapping` merge. Because a spec is a plain object, you can build it up conditionally, share fragments across charts, and [store or serialize it](/sdk-next/concepts/serializable-spec).

```tsx theme={null}
// Build in stages — each call returns a new spec.
let spec = createSpec({ x: 'month', y: 'revenue' });
spec = pipe(spec, geom.line(), scale.x(), scale.y());
if (showAverage) {
  spec = pipe(spec, geom.rule({ stat: stat.mean() }));
}
```

## The parts of a spec

A spec brings together six kinds of part. Each has its own concept page:

| Part                                                        | What it decides                                    | Builder                 |
| ----------------------------------------------------------- | -------------------------------------------------- | ----------------------- |
| [Mapping](/sdk-next/concepts/mappings)                      | Which data columns feed which aesthetics           | `mapping`, `createSpec` |
| [Geoms](/sdk-next/concepts/geoms)                           | The shapes drawn — line, bar, point, area, rule    | `geom.*`                |
| [Scales](/sdk-next/concepts/scales)                         | How data values become positions, colors and sizes | `scale.*`               |
| [Coordinate systems](/sdk-next/concepts/coordinate-systems) | The plane observations are drawn in                | `coord.*`               |
| [Statistics](/sdk-next/advanced/statistics)                 | Per-layer summaries (count, mean, smoothing)       | `stat.*`                |
| [Transforms](/sdk-next/advanced/transforms)                 | Reshaping the data before it's drawn               | `transform.*`           |

Plus [configuration](/sdk-next/config/index) (`config`) for chart chrome — titles, axes, legend, appearance.

## Compile, then render

A spec is a *description*; it isn't yet pixels. Turning it into a chart happens in two stages, one per package:

```mermaid theme={null}
flowchart LR
  S[Spec] --> C[viz-engine: compile]
  D[Data] --> C
  C --> R[CompiledSpec]
  R --> P[react-renderer: paint]
  P --> SVG[SVG chart]
```

1. **Compile (`@graphysdk/viz-engine`).** The engine takes your spec plus your data and produces a `CompiledSpec`: scales are resolved to concrete domains, statistics are run, every observation is placed in a normalized `[0,1]` position space, and guides (axes, legend) are worked out. This stage is pure data-in, data-out — no DOM.
2. **Render (`@graphysdk/react-renderer`).** The renderer takes the `CompiledSpec` and paints it: it lays out pixel rectangles, formats numbers and dates for the locale, applies the theme, and wires up hover and animation.

In a React app you don't call the compiler yourself — `<GraphProvider>` does it for you and recompiles when the spec, data or theme change:

```tsx theme={null}
import { GraphProvider, GraphRenderer } from '@graphysdk/react-renderer';

<GraphProvider input={spec} data={data} theme="light">
  <GraphRenderer />
</GraphProvider>;
```

## Why the split matters

The split between the two packages shapes how you use the SDK:

* **Data lives outside the spec.** A spec references columns by name; the actual rows are passed to `<GraphProvider>` separately. One spec can draw many datasets.
* **The compiler never formats.** It emits raw values and *descriptors*; the renderer turns them into locale-aware text. That's why formatting options live on the renderer side.
* **Scales are resolved once, at compile time.** This is why you declare scales explicitly in the spec — the engine can't paint a position it was never told how to compute.

Authoring concerns (mapping, geoms, scales, stats) live in viz-engine; presentation concerns (theme, formatting, interactivity, layout) live in react-renderer.

## Next

* [Scales](/sdk-next/concepts/scales) — the one part you must always declare
* [Serializable spec](/sdk-next/concepts/serializable-spec) — a spec is plain JSON you can persist and reload
