DEV Community

Cover image for Embed Charts in Markdown Without Chart.js: SVG, Data URIs, and What Actually Works
FSCSS for ProvChart

Posted on

Embed Charts in Markdown Without Chart.js: SVG, Data URIs, and What Actually Works

Markdown is great until you want a real chart in a README or docs site. Most people reach for a screenshot, a hosted PNG, or a heavy client library. ProvChart takes another path: POST /api/v1/generate-svg returns a self-contained SVG (and optional data URI) from your data—no js on the page, no image CDN required.

This article covers what different Markdown platforms allow, practical embed patterns, and when pure SVG is the better default.


The idea

  1. Send series data to ProvChart’s SVG endpoint.
  2. Get back:
    • svg — full <svg xmlns="...">...</svg> string
    • dataUridata:image/svg+xml;base64,...
  3. Embed with whatever your platform supports.

Platform reality: not every Markdown engine is equal

Approach GitHub README Many static docs Personal / controlled MD Notes
Data URI ![](data:image/svg+xml;...) Often fragile Sometimes OK Often OK Long base64 can break or get sanitized
<img src="data:..."> Limited Varies Often OK Same length / sanitize issues
File ![](./chart.svg) ✅ Reliable ✅ Reliable ✅ Best default Commit the SVG; no key in the repo
Inline <svg> ❌ Usually stripped Sometimes ✅ If HTML allowed Great for MDX / some SSGs
Raster PNG/WebP Use only if the host blocks SVG

Takeaway: Prefer a committed .svg file for GitHub and public docs. Use data URIs for quick demos. Use inline SVG only where the engine allows raw HTML (MDX, some wikis, your own site).


Pattern 1 — Generate SVG (API)

curl -s -X POST "https://provchart-api.devtem.org/api/v1/generate-svg" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "type": "line",
    "series": [
      { "name": "Stars", "color": "#8b7bff", "points": [12, 18, 25, 40, 55] }
    ],
    "axisX": ["Jan", "Feb", "Mar", "Apr", "May"],
    "width": 640,
    "height": 280
  }'
Enter fullscreen mode Exit fullscreen mode

Response shape:

{
  "success": true,
  "svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" ...>...</svg>",
  "dataUri": "data:image/svg+xml;base64,..."
}
Enter fullscreen mode Exit fullscreen mode

Keep the key in CI secrets.


Pattern 2 — Embed options

A. Image from file (recommended for README)

![Stars over time](./docs/charts/stars.svg)
Enter fullscreen mode Exit fullscreen mode

Save the svg field from the API into that path and commit it.

B. Data URI (quick, not always portable)

![Stars over time](data:image/svg+xml;base64,AAAA...)
Enter fullscreen mode Exit fullscreen mode

If the preview is blank, the host likely truncated or blocked the URI—switch to a file.

C. Inline SVG (when HTML is allowed)

<!-- MDX / some static generators -->
<div>
  <!-- paste the svg string from the API -->
</div>
Enter fullscreen mode Exit fullscreen mode

GitHub README will generally not render arbitrary inline SVG.

D. Raster only if you must

If a platform blocks SVG entirely, convert once in your pipeline (e.g. local tool) and commit PNG. That’s a fallback—not the default for your docs site, where SVG is usually better.


Advantages of the SVG path

  1. No chart runtime in the doc UI — readers don’t download Chart.js to see a trend.
  2. No third-party image host — nothing uploaded to a random CDN for a badge.
  3. Sharp at any zoom — useful for docs and retina displays.
  4. Same data model as HTML chartstype, series, axisX match ProvChart’s generate API.
  5. CI-friendly — regenerate docs/charts/*.svg when metrics change; commit the artifact.
  6. Fits the “pipeline” story — compile data → paint geometry; for Markdown the paint target is SVG instead of CSS-in-page.

When pure SVG is the better choice

  • Personal knowledge bases, Notion-export-style vaults, and static doc sites you control
  • GitHub/GitLab asset charts (versioned next to the repo)
  • Design systems / architecture READMEs that should stay dependency-light
  • Agent or cron jobs that refresh charts without opening a browser

Use HTML + CSS (/api/v1/generate) when the chart lives inside a web app where theme tokens and layout already use CSS.


Minimal Node helper (write a file)

import fs from "node:fs";

const res = await fetch("https://provchart-api.devtem.org/api/v1/generate-svg", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": process.env.PROVCHART_API_KEY,
  },
  body: JSON.stringify({
    type: "area",
    series: [{ name: "Views", color: "#4fd8c4", points: [10, 25, 40, 55, 48] }],
    axisX: ["Mon", "Tue", "Wed", "Thu", "Fri"],
    width: 640,
    height: 240,
  }),
});

const data = await res.json();
if (!data.success) throw new Error(data.error);
fs.writeFileSync("docs/charts/views.svg", data.svg);
Enter fullscreen mode Exit fullscreen mode

Point Markdown at ./docs/charts/views.svg.


Troubleshooting

Symptom Likely cause Fix
Broken image in README Data URI too long / blocked Commit .svg + relative path
401 Bad or revoked key New key in Dashboard → Developer API
429 Monthly limit Upgrade plan or wait for reset
Empty graphic Bad payload Check series[].points and axisX

Links

Markdown support for data: URIs is inconsistent. For public README and most docs, generate SVG - save file - ![](./chart.svg). Use data URIs for experiments; use inline SVG only where your engine allows HTML. That’s how you get charts in docs.


Top comments (0)