UNDER THE HOOD
No JavaScript. No SVG. No images.
Pure HTML tables
One JSON payload renders a complete chart — axis, scale, values, legends and source link — as nested tables with inline styles.
Zero dependencies
Pure HTML output. Nothing to ship, nothing to break.
Every client
Static tables with inline styles render wherever HTML email renders — Outlook included.
Auto-fit width
When data overflows max-width, bar and separator widths are recomputed to fit — and the y-axis hides to gain space.
108 kB build
Separate sourcemaps kept out of the package — only what consumers need gets shipped.
100% test coverage
Every rendering path is snapshot-tested against real MJML output.
CJS + ESM + types
CommonJS, ES modules and TypeScript declarations are built and published — drop it into any bundler or require it directly.
MIT licensed
Free to use in commercial and personal projects — attribution appreciated, never required.
HTML ATTRIBUTES
One component, eleven attributes.
| attribute | type | default | description |
|---|---|---|---|
| uid | string | "" | Suffixes chart CSS classes — style multiple charts in the same email independently |
| stacked | boolean | "false" | Stack dataset values in a single column |
| font-family | string | "inherit" | Font family for every text element — can also be set from <mj-all> |
| axis-color | string | "#d4d4d4" | CSS color of axis and scale numbers |
| height | number | "200" | Chart height in pixels |
| bar-width | number | "30" | Bar width in pixels |
| separator-width | number | "30" | Datasets separator width in pixels |
| step-count | number | "5" | Steps on the chart scale — below 2, no steps are displayed |
| show-values | boolean | "true" | Display values above each bar |
| align-legends | boolean | "false" | Vertically align legend labels |
| max-width | number | "600" | Max chart width — on overflow, bar and separator widths are recomputed to fit |
CSS CLASSES
Style it with CSS classes.
Every element is addressable from your own CSS — pass a uid to style multiple charts independently.
- mjbc
- mjbc__title
- mjbc__source
- mjbc__label
- mjbc__legend
- mjbc__step
- mjbc__bar
What each class targets
Classes follow the BEM convention: mjbc is the block, __element the part. With a uid, every class becomes mjbc<uid>__element.
| class | rendered element | default styling |
|---|---|---|
| mjbc | root <table> wrapping the whole chart | border-collapse:collapse |
| mjbc__title | <td> holding the chart title text | height:40px · font-size:20px · bold · centered |
| mjbc__source | <td> holding the source link (when set) | height:30px · font-size:12px · color:#3e3e3e |
| mjbc__label | <td> holding a dataset label, below each bar group | height:30px · font-size:14px · centered |
| mjbc__legend | <span> holding a legend entry (chip + label) | inline-block · padding:0 10px · font-size:14px |
| mjbc__step | <td> holding a y-axis step number | color from axis-color · right-aligned · 33px wide |
| mjbc__bar | <td> (or nested <table>) holding a single bar value | dimensions computed at render time — style colors, not sizes |
Where each class sits
Two ways to override
mj-class (recommended) injects your styles into the generated style attributes — highest priority. mj-style appends a stylesheet with low priority, best for global tweaks across every chart.
<!-- global, low priority: every chart -->
<mj-style>
.mjbc__title { color: lightcoral; }
.mjbc__legend { font-size: 12px; }
</mj-style>
<!-- per chart, high priority: uid-namespaced -->
<mj-attributes>
<mj-class name="mjbc1__title" color="lightcoral" font-weight="bold"/>
<mj-class name="mjbc1__label" color="rebeccapurple" font-family="Menlo"/>
<mj-class name="mjbc2__legend" font-size="12px"/>
</mj-attributes>
<mj-bar-chart uid="1">{ JSON payload }</mj-bar-chart>
<mj-bar-chart uid="2" stacked align-legends>{ JSON payload }</mj-bar-chart>
