<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ProvChart</title>
    <description>The latest articles on DEV Community by ProvChart (provchart).</description>
    <link>https://dev.to/provchart</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F14466%2Fb4fe5b40-3c9d-46ca-9537-1880fc4bea3c.png</url>
      <title>DEV Community: ProvChart</title>
      <link>https://dev.to/provchart</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/provchart"/>
    <language>en</language>
    <item>
      <title>Embed Charts in Markdown Without Chart.js: SVG, Data URIs, and What Actually Works</title>
      <dc:creator>FSCSS </dc:creator>
      <pubDate>Mon, 24 Aug 2026 11:08:20 +0000</pubDate>
      <link>https://dev.to/provchart/embed-charts-in-markdown-without-chartjs-svg-data-uris-and-what-actually-works-39pi</link>
      <guid>https://dev.to/provchart/embed-charts-in-markdown-without-chartjs-svg-data-uris-and-what-actually-works-39pi</guid>
      <description>&lt;p&gt;Markdown is great until you want a &lt;strong&gt;real chart&lt;/strong&gt; in a README or docs site. Most people reach for a screenshot, a hosted PNG, or a heavy client library. ProvChart takes another path: &lt;strong&gt;&lt;code&gt;POST /api/v1/generate-svg&lt;/code&gt;&lt;/strong&gt; returns a self-contained SVG (and optional data URI) from your data—no js on the page, no image CDN required.&lt;/p&gt;

&lt;p&gt;This article covers &lt;strong&gt;what different Markdown platforms allow&lt;/strong&gt;, practical embed patterns, and when pure SVG is the better default.&lt;/p&gt;




&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Send series data to ProvChart’s SVG endpoint.
&lt;/li&gt;
&lt;li&gt;Get back:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;svg&lt;/code&gt; — full &lt;code&gt;&amp;lt;svg xmlns="..."&amp;gt;...&amp;lt;/svg&amp;gt;&lt;/code&gt; string
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dataUri&lt;/code&gt; — &lt;code&gt;data:image/svg+xml;base64,...&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Embed with whatever your platform supports.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Platform reality: not every Markdown engine is equal
&lt;/h2&gt;

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

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




&lt;h2&gt;
  
  
  Pattern 1 — Generate SVG (API)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://provchart-api.devtem.org/api/v1/generate-svg"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "type": "line",
    "series": [
      { "name": "Stars", "color": "#8b7bff", "points": [12, 18, 25, 40, 55] }
    ],
    "axisX": ["Jan", "Feb", "Mar", "Apr", "May"],
    "width": 640,
    "height": 280
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"svg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;svg xmlns=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;http://www.w3.org/2000/svg&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ...&amp;gt;...&amp;lt;/svg&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dataUri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"data:image/svg+xml;base64,..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the key in CI secrets.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 2 — Embed options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A. Image from file (recommended for README)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;Stars over time&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./docs/charts/stars.svg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the &lt;code&gt;svg&lt;/code&gt; field from the API into that path and commit it.&lt;/p&gt;

&lt;h3&gt;
  
  
  B. Data URI (quick, not always portable)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;Stars over time&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;data:image/svg+xml;base64,AAAA...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the preview is blank, the host likely truncated or blocked the URI—switch to a file.&lt;/p&gt;

&lt;h3&gt;
  
  
  C. Inline SVG (when HTML is allowed)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- MDX / some static generators --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- paste the svg string from the API --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub README will generally &lt;strong&gt;not&lt;/strong&gt; render arbitrary inline SVG.&lt;/p&gt;

&lt;h3&gt;
  
  
  D. Raster only if you must
&lt;/h3&gt;

&lt;p&gt;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 &lt;em&gt;your&lt;/em&gt; docs site, where SVG is usually better.&lt;/p&gt;




&lt;h2&gt;
  
  
  Advantages of the SVG path
&lt;/h2&gt;

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




&lt;h2&gt;
  
  
  When pure SVG is the better choice
&lt;/h2&gt;

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

&lt;p&gt;Use &lt;strong&gt;HTML + CSS&lt;/strong&gt; (&lt;code&gt;/api/v1/generate&lt;/code&gt;) when the chart lives &lt;strong&gt;inside a web app&lt;/strong&gt; where theme tokens and layout already use CSS.&lt;/p&gt;




&lt;h2&gt;
  
  
  Minimal Node helper (write a file)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;docs/charts/views.svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point Markdown at &lt;code&gt;./docs/charts/views.svg&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Broken image in README&lt;/td&gt;
&lt;td&gt;Data URI too long / blocked&lt;/td&gt;
&lt;td&gt;Commit &lt;code&gt;.svg&lt;/code&gt; + relative path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;401&lt;/td&gt;
&lt;td&gt;Bad or revoked key&lt;/td&gt;
&lt;td&gt;New key in Dashboard → Developer API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;429&lt;/td&gt;
&lt;td&gt;Monthly limit&lt;/td&gt;
&lt;td&gt;Upgrade plan or wait for reset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Empty graphic&lt;/td&gt;
&lt;td&gt;Bad payload&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;series[].points&lt;/code&gt; and &lt;code&gt;axisX&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SVG API: &lt;a href="https://chart.devtem.org/docs#dev-api-svg" rel="noopener noreferrer"&gt;ProvChart docs – generate-svg&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Guide: &lt;a href="https://chart.devtem.org/guides/charts-in-markdown" rel="noopener noreferrer"&gt;Charts in Markdown &amp;amp; docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pricing (quota): &lt;a href="https://chart.devtem.org/pricing" rel="noopener noreferrer"&gt;chart.devtem.org/pricing&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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




</description>
      <category>markdown</category>
      <category>documentation</category>
      <category>showdev</category>
      <category>provchat</category>
    </item>
    <item>
      <title>Email Charts with the ProvChart API</title>
      <dc:creator>FSCSS </dc:creator>
      <pubDate>Sun, 23 Aug 2026 10:41:23 +0000</pubDate>
      <link>https://dev.to/provchart/email-charts-with-the-provchart-api-3gni</link>
      <guid>https://dev.to/provchart/email-charts-with-the-provchart-api-3gni</guid>
      <description>&lt;p&gt;Most chart libraries assume a browser that runs JavaScript. Email clients don't give you that. If you want a chart in a weekly digest, you need something that renders without a script tag, or a step that turns a chart into an image before it ever reaches an inbox.&lt;/p&gt;

&lt;p&gt;That's the problem ProvChart's API is built for. It returns HTML and CSS, generated server-side, and you decide from there whether to rasterize it into a PNG or embed a tightly controlled snippet. This post walks through the flow, the chart types that actually work in a digest, and a handful of practical tips for running this in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why email is different
&lt;/h2&gt;

&lt;p&gt;The inbox never needs to see your API key or your chart library. It only needs a static image and a link back to the live dashboard. Everything else, the data pull, the payload mapping, the API call, happens on your server or in a worker, before the send.&lt;/p&gt;

&lt;h2&gt;
  
  
  The flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Pull analytics for the period, from your API, warehouse, or database&lt;/li&gt;
&lt;li&gt;Map the rows into a ProvChart payload (&lt;code&gt;type&lt;/code&gt;, &lt;code&gt;series&lt;/code&gt;, &lt;code&gt;axisX&lt;/code&gt;, &lt;code&gt;theme&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/v1/generate&lt;/code&gt; with your API key&lt;/li&gt;
&lt;li&gt;Turn the returned &lt;code&gt;html&lt;/code&gt; and &lt;code&gt;css&lt;/code&gt; into a PNG (or store the snippet)&lt;/li&gt;
&lt;li&gt;Embed the result in your email template with a CTA back to the dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Mapping analytics to a payload
&lt;/h2&gt;

&lt;p&gt;Keep a single mapper function so every report type shares the same shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;analyticsToPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;series&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Revenue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#8b7bff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;revenueByDay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#4fd8c4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ordersByDay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;axisX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;revenueByDay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#0c0a16&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a1628&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#eae7f5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#837da0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#8b7bff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap the data source underneath it, but keep the function stable. Weekly, monthly, and alert emails all stay consistent this way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chart types that actually work in a digest
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trend over 7–30 days&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;line&lt;/code&gt; / &lt;code&gt;area&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Readable as a simple story&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Channel or category mix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clear comparison&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Goal or conversion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gauge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One number, high impact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Share of total&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stackedbar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Good for mix breakdowns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Skip dense scatter plots or multi-combo charts in email. Save those for the product UI, where someone can actually zoom in and interact.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flveliy5zlw748gamt9ym.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flveliy5zlw748gamt9ym.jpg" alt="ProvChart bar chart example" width="720" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ten things worth doing
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Generate server-side.&lt;/strong&gt; The send job or worker owns the API key, the payload, and the output. Never call the API from the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer images for email clients.&lt;/strong&gt; The reliable path is: analytics → API → html/css → headless render (Playwright or Puppeteer) → PNG → CDN → &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; in the template. This works across Gmail, Outlook, and Apple Mail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design for one glance.&lt;/strong&gt; Each chart should answer one question: did revenue rise, which channel led. Cap it at one or two series and 7–14 data points. Put the headline number in the subject line or preheader.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache by payload hash.&lt;/strong&gt; Identical type, series, labels, and theme should produce identical output. Cache the html/css or the PNG so repeat sends don't burn quota.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check usage before bulk sends.&lt;/strong&gt; Hit &lt;code&gt;GET /api/v1/usage&lt;/code&gt; first. If &lt;code&gt;remaining&lt;/code&gt; is low, drop the optional charts or fall back to number-only blocks rather than hitting &lt;code&gt;MONTHLY_LIMIT_REACHED&lt;/code&gt; mid-campaign.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theme for email, not the admin UI.&lt;/strong&gt; Use higher contrast and a background that matches your template. A dedicated &lt;code&gt;theme&lt;/code&gt; object for digests versus in-app charts goes a long way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always add a CTA.&lt;/strong&gt; A simple "Full dashboard →" link under the chart. The email is the snapshot; the app is where people explore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep one layout per digest type.&lt;/strong&gt; Hero metric, one trend chart, one breakdown, a few bullets, then the CTA. Change the data, not the structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate before you generate.&lt;/strong&gt; Empty series, non-numeric points, or oversized arrays waste generations. Enforce max points and required fields in the mapper itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy the key.&lt;/strong&gt; Production frontends shouldn't hold the secret key at all. Route browser requests through your own backend before they reach ProvChart.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A minimal server-side example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;analyticsToPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;weeklyReport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://provchart-api.devtem.org/api/v1/generate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROVCHART_API_KEY&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Option A: rasterize data.html + data.css into a PNG, upload it, then &amp;lt;img src="..."&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// Option B: store the snippet for a web version of the same report&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Do / Don't
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate server-side&lt;/li&gt;
&lt;li&gt;Map analytics to a fixed payload shape&lt;/li&gt;
&lt;li&gt;Cache by hash&lt;/li&gt;
&lt;li&gt;Prefer line, bar, or gauge charts&lt;/li&gt;
&lt;li&gt;Link back to the live dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Don't&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call the API from the inbox&lt;/li&gt;
&lt;li&gt;Build ad-hoc JSON for every send&lt;/li&gt;
&lt;li&gt;Regenerate identical charts&lt;/li&gt;
&lt;li&gt;Pack five dense series into a digest&lt;/li&gt;
&lt;li&gt;Expect full interactivity in email&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chart.devtem.org/dashboard" rel="noopener noreferrer"&gt;Create an API key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chart.devtem.org/docs#dev-api-overview" rel="noopener noreferrer"&gt;Developer API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chart.devtem.org/guides/build-generator" rel="noopener noreferrer"&gt;Build your own generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chart.devtem.org/pricing" rel="noopener noreferrer"&gt;Plans and monthly limits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>provchart</category>
      <category>api</category>
      <category>email</category>
      <category>css</category>
    </item>
    <item>
      <title>Introduction to ProvChart — Charts Without the JavaScript Tax</title>
      <dc:creator>FSCSS </dc:creator>
      <pubDate>Sat, 22 Aug 2026 08:28:32 +0000</pubDate>
      <link>https://dev.to/provchart/introduction-to-provchart-charts-without-the-javascript-tax-4l38</link>
      <guid>https://dev.to/provchart/introduction-to-provchart-charts-without-the-javascript-tax-4l38</guid>
      <description>&lt;h2&gt;
  
  
  Introduction to ProvChart
&lt;/h2&gt;

&lt;p&gt;Most charting libraries ask you to ship &lt;strong&gt;40–150 kb of JavaScript&lt;/strong&gt;, wait for hydration, and hope crawlers eventually see the chart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ProvChart&lt;/strong&gt; takes a different path.&lt;/p&gt;

&lt;p&gt;It turns your data into &lt;strong&gt;CSS custom properties&lt;/strong&gt;. The browser paints the chart with native CSS (&lt;code&gt;clip-path&lt;/code&gt;, gradients, and a few carefully chosen rules). The result is a chart that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loads with the &lt;strong&gt;first paint&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Ships &lt;strong&gt;zero chart library&lt;/strong&gt; to the page&lt;/li&gt;
&lt;li&gt;Is &lt;strong&gt;fully visible to search engines&lt;/strong&gt; on the first crawl&lt;/li&gt;
&lt;li&gt;Can still be updated by changing CSS variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No canvas. No SVG charting runtime. No “wait for JS.”&lt;/p&gt;




&lt;h2&gt;
  
  
  The idea in one sentence
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Data → CSS variables → browser paints the shape.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You (or the API) compile points into variables like &lt;code&gt;--p1&lt;/code&gt;, &lt;code&gt;--p2&lt;/code&gt;, ….&lt;br&gt;&lt;br&gt;
A pure CSS structure uses those variables to draw lines, areas, bars, or gauges.&lt;br&gt;&lt;br&gt;
After that, the page doesn’t need a charting library at all.&lt;/p&gt;


&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Self-contained HTML + CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Runtime on the page&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None for the chart itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Line, area, bar, stacked bar, horizontal bar, scatter, combo, gauge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Themes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dark, light, midnight, or fully custom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SEO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chart markup is in the first HTML response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Change CSS variables (or regenerate) — no virtual DOM chart layer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is also a &lt;strong&gt;Developer API&lt;/strong&gt;: send JSON, get back &lt;code&gt;{ html, css }&lt;/code&gt;, inject into any page or pipeline.&lt;/p&gt;


&lt;h2&gt;
  
  
  Who it’s for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Teams that care about &lt;strong&gt;LCP, TTI, and bundle size&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Marketing and docs sites that need &lt;strong&gt;crawlable&lt;/strong&gt; charts&lt;/li&gt;
&lt;li&gt;Static or mostly-static pages that still need real data viz&lt;/li&gt;
&lt;li&gt;Backends that want to generate charts for &lt;strong&gt;emails, reports, or static builds&lt;/strong&gt; without shipping a client-side chart library&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need heavy interaction, streaming of thousands of points, or complex JS-driven animations, a traditional library may still fit better. ProvChart optimizes for &lt;strong&gt;performance, simplicity, and SEO&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Quick taste (API)
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://provchart-api.devtem.org/api/v1/generate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;series&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Revenue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#8b7bff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#4fd8c4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;axisX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Feb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Apr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// data.html + data.css → inject into the page&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe2knt0kx1gh4ozzdrwjc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe2knt0kx1gh4ozzdrwjc.jpg" alt="ProvChart chart example" width="720" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free accounts get a few &lt;strong&gt;test generations&lt;/strong&gt; so you can try the full flow.&lt;br&gt;&lt;br&gt;
Pro and Business unlock higher monthly limits for production use.&lt;/p&gt;


&lt;h2&gt;
  
  
  Open source core + hosted product
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rendering core:&lt;/strong&gt; &lt;a href="https://github.com/fscss-ttr/st-core.fscss" rel="noopener noreferrer"&gt;st-core.fscss&lt;/a&gt; (MIT) — pure CSS chart primitives for the FSCSS ecosystem
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosted product:&lt;/strong&gt; &lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;ProvChart&lt;/a&gt; — visual builder, dashboard, Developer API, billing (by &lt;a href="https://devtem.org" rel="noopener noreferrer"&gt;DevTemple&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use the open-source core on its own, or the hosted API when you want keys, limits, and multi-series generation without maintaining the engine yourself.&lt;/p&gt;


&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://chart.devtem.org" rel="noopener noreferrer"&gt;chart.devtem.org&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Product home&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://chart.devtem.org/get-started" rel="noopener noreferrer"&gt;Get started&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Free visual builder (no signup for single-series)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://chart.devtem.org/docs" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Core + Developer API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://chart.devtem.org/pricing" rel="noopener noreferrer"&gt;Pricing&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Free / Pro / Business&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  What’s next from this org
&lt;/h2&gt;

&lt;p&gt;We’ll publish short, practical posts on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building a small chart generator with the API
&lt;/li&gt;
&lt;li&gt;Using ProvChart in &lt;strong&gt;email digests&lt;/strong&gt; (server-side → image/snapshot)
&lt;/li&gt;
&lt;li&gt;Performance and SEO notes vs typical JS chart stacks
&lt;/li&gt;
&lt;li&gt;Themes, multi-series patterns, and real dashboard layouts
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you care about charts that stay fast and crawlable, you’re in the right place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ProvChart — charts without the JavaScript tax.&lt;/strong&gt;&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://chart.devtem.org" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;chart.devtem.org&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>sharepoint</category>
      <category>seo</category>
      <category>performance</category>
      <category>provchart</category>
    </item>
  </channel>
</rss>
