---
audience: developers
summary: Author native and image charts across DOCX, PPTX, and XLSX.
title: Charts
path: tutorials/charts
status: published
---


# Tutorial: Charts

You'll author every chart kind ScaiScribe supports natively, then deliberately fall through to the image pipeline for one of the more exotic kinds. By the end you'll know which `render_mode` to pick for which situation.

## Setup

```python
import os
from scaiscribe import Client, ScaiKeyAuth

client = Client(
    "https://scaiscribe.scailabs.ai",
    auth=ScaiKeyAuth(
        client_id=os.environ["SCAIKEY_CLIENT_ID"],
        client_secret=os.environ["SCAIKEY_CLIENT_SECRET"],
    ),
    default_tenant_id=os.environ["SCAISCRIBE_TENANT_ID"],
)
```

## Example 1 — Column chart in DOCX (native)

```python
doc = client.create_document(format="docx", theme="office", title="Q4 review")

client.add_element(doc.doc_id, {
    "type": "heading", "level": 1, "text": "Quarterly performance"
})

client.add_element(doc.doc_id, {
    "type": "chart",
    "kind": "column",
    "title": "Revenue by region",
    "render_mode": "native",
    "data": {
        "categories": ["Q1", "Q2", "Q3", "Q4"],
        "series": [
            {"name": "EMEA", "values": [1200, 1450, 1600, 1800]},
            {"name": "AMER", "values": [1000, 1100, 1300, 1400]},
        ],
    },
})

result = client.finalize(doc.doc_id, formats=["docx"])
```

The output `.docx` contains `word/charts/chart1.xml` — a fully editable native Word chart. Double-click it in Word and you can change the data, restyle, or copy it to PowerPoint as a native chart.

## Example 2 — Pie chart in PPTX (native via pptxgenjs)

```python
doc = client.create_document(format="pptx", theme="office", title="Quarterly")

client.add_element(doc.doc_id, {
    "op": "add_slide",
    "slide": {
        "layout": "data_feature",
        "elements": [
            {"type": "slide_title", "text": "Q4 spend"},
            {
                "type": "chart",
                "kind": "pie",
                "title": "Spend categories",
                "data": {
                    "categories": ["Salaries", "Cloud", "Travel", "Other"],
                    "series": [{"name": "Spend", "values": [220000, 45000, 12000, 8000]}],
                },
            },
        ],
    },
})

result = client.finalize(doc.doc_id, formats=["pptx"])
```

PPTX uses pptxgenjs's native chart writer, which has broader coverage than the DOCX/XLSX hand-emitted path. All categorical and scatter kinds render natively.

## Example 3 — Column chart in XLSX (native)

```python
doc = client.create_document(format="xlsx", theme="office", title="Sales data")

# Add a sheet with data
client.patch_document(doc.doc_id, [{
    "op": "add_sheet",
    "sheet": {
        "name": "Data",
        "cells": [
            {"address": "A1", "value": "Region", "style_id": "header_row"},
            {"address": "B1", "value": "Q1", "style_id": "header_row"},
            {"address": "B2", "value": 1200},
            {"address": "B3", "value": 1000},
            # ...
        ],
    },
}])

# Anchor a chart at E2
sheet_id = client.get_document(doc.doc_id).spec["sheets"][0]["sheet_id"]
client.patch_document(doc.doc_id, [{
    "op": "add_sheet_chart",
    "sheet_id": sheet_id,
    "chart": {
        "kind": "column",
        "anchor": "E2",
        "title": "Revenue by region",
        "render_mode": "native",
        "data": {
            "categories": ["Q1", "Q2", "Q3", "Q4"],
            "series": [
                {"name": "EMEA", "values": [1200, 1450, 1600, 1800]},
                {"name": "AMER", "values": [1000, 1100, 1300, 1400]},
            ],
        },
    },
}])

result = client.finalize(doc.doc_id, formats=["xlsx"])
```

The result is an `.xlsx` with `xl/charts/chart1.xml` + a drawing anchor — a real native Excel chart that respects axis sorting, series colours, and double-click-to-edit.

## Example 4 — Sankey chart in DOCX (image fallback)

```python
client.add_element(doc.doc_id, {
    "type": "chart",
    "kind": "sankey",
    "title": "Customer journey",
    "data": {
        "nodes": [
            {"name": "Landing"},
            {"name": "Signup"},
            {"name": "Activated"},
            {"name": "Paid"},
        ],
        "links": [
            {"source": "Landing", "target": "Signup", "value": 1000},
            {"source": "Signup", "target": "Activated", "value": 600},
            {"source": "Activated", "target": "Paid", "value": 320},
        ],
    },
})
```

Sankey isn't in the DOCX native matrix. The `auto` render_mode (the default) detects this and renders an ECharts SVG instead — embedded as a `<w:drawing>` image in the .docx. Visually accurate, themed to your `chart_palette`, but not editable as a native chart.

## When to override `render_mode`

| Want | Set | Why |
|---|---|---|
| Default behaviour (native if possible, image otherwise) | `"auto"` or omit | The right answer 95% of the time. |
| Force an image even for natively-supported kinds | `"image"` | ECharts can be visually better than native for some kinds (e.g. PPTX scatter rendering can disappoint at small sizes). |
| Force a native chart and crash visibly if it can't | `"native"` | You want the chart to be editable downstream, and would rather see an error than ship an image. |

A `render_mode: "native"` request that hits an unsupported kind produces a placeholder paragraph + a `NATIVE_FALLBACK` warning in the fidelity envelope. Not a render failure — a visible signal.

## Theming chart colours

Series colours come from the active theme's `components.chart_palette`. In `office`:

```json
"chart_palette": ["#2B579A", "#5B9BD5", "#70AD47", "#FFC000", "#ED7D31", "#A5A5A5"]
```

Override per-series by setting `series[i].color`:

```python
"series": [
    {"name": "Target", "values": [100, 100, 100], "color": "#FF0000"},
    {"name": "Actual", "values": [85, 92, 98]},  # picks up palette[1]
]
```

## Common patterns

### Stacked column

```python
{
    "kind": "stacked_column",
    "data": {
        "categories": ["Q1", "Q2", "Q3", "Q4"],
        "series": [
            {"name": "Recurring", "values": [80, 100, 120, 140]},
            {"name": "Services",  "values": [40,  50,  60,  70]},
        ],
    },
}
```

Native in PPTX and XLSX; image fallback in DOCX.

### Line over time

```python
{
    "kind": "line",
    "data": {
        "categories": ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
        "series": [{"name": "Signups", "values": [50, 60, 75, 92, 110, 135]}],
    },
}
```

Native in all three formats.

### Scatter

```python
{
    "kind": "scatter",
    "data": {
        "series": [
            {"name": "Sites", "points": [{"x": 10, "y": 200}, {"x": 25, "y": 180}, ...]},
        ],
    },
}
```

Native in PPTX and XLSX; image in DOCX.

## Gotchas

- **Series count vs. native rendering** — native renderers handle up to ~6 series cleanly. Beyond that, even when "native" is supported, the legend and colours get cramped. Consider `render_mode: "image"` for high-series-count charts to let ECharts compose the layout.
- **Empty data** — a chart with no points renders as an empty chart frame (not an error). Validate upstream.
- **Title visibility** — omitting `title` is fine; the chart frame shrinks. Setting `title: ""` produces a chart with an empty title bar (Excel quirk). Prefer omission.
- **Categories type** — categories must be strings even when they're conceptually numeric (years, IDs). Pass `["2024", "2025"]`, not `[2024, 2025]`.

## Where to next

- [Charts concept](/docs/scaiscribe/concepts/charts) — full support matrix + theming.
- [Themes](/docs/scaiscribe/concepts/themes) — chart_palette and how it propagates.
- [Reference → API](/docs/scaiscribe/reference/api) — `Chart` schema.
