Markdown & MDX
EmbedHub renders .md and .mdx files in-page so project documentation is
readable without downloading or switching tools.
Upload a .md or .mdx to a project's Documents or Releases section
(via the web, embedhub push, or the GitHub Action) and a small eye icon
appears next to the file. Clicking it opens an in-page reader.
.mdrenders as plain markdown..mdxrenders markdown plus a small set of interactive components.
Plain markdown
.md files render with GitHub-flavored markdown: tables, task lists,
strikethrough, autolinks, fenced code blocks.
# Release 1.4
## What's new
- New flash mode for ESP32-S3
- Faster boot — typical ~1.2 s
- ~~Old serial logger~~ removed
## Test status
| Board | Boot | Network | Sensors |
| ----------- | ---- | ------- | ------- |
| esp32-s3 | ✅ | ✅ | ✅ |
| esp32-c6 | ✅ | ✅ | ⚠️ |
## Checklist
- [x] sign release tag
- [x] upload firmware
- [ ] update changelog
MDX adds components
.mdx is markdown plus a small library of components you can drop into
the page. Save the file with an .mdx extension to opt in.
<Note>
A coloured callout for highlighting context. Variants: info, warn,
error. The body accepts markdown.
<Note variant="info" title="Heads up">
Run `embedhub config set api-key …` once before pushing from CI.
</Note>
<Note variant="warn">
This board ships with the bootloader **disabled** by default.
</Note>
<Note variant="error" title="Don't do this">
Never commit `.env` to source control.
</Note>
<Chart>
Renders an interactive chart inline. Hover for tooltips; click items in the legend to toggle individual series.
| Prop | Type | Default | Description |
|---|---|---|---|
type | "line" | "bar" | "area" | line | Chart style. |
data | Array<object> | — | Required. Each object is one row. |
xKey | string | "x" | Field on each row to use as the x-axis label. |
yKey | string | string[] | "y" | Field(s) to plot. Pass an array for multi-series. |
title | string | — | Optional caption above the chart. |
height | number | 280 | Chart height in pixels. |
Single-series bar:
<Chart
type="bar"
title="Defects per build"
xKey="build"
yKey="defects"
data={[
{ build: '#101', defects: 3 },
{ build: '#102', defects: 1 },
{ build: '#103', defects: 5 },
{ build: '#104', defects: 0 },
{ build: '#105', defects: 2 },
]}
/>
Single-series line:
<Chart
type="line"
title="Boot time (ms) by build"
xKey="build"
yKey="bootMs"
data={[
{ build: '1.0', bootMs: 1820 },
{ build: '1.1', bootMs: 1640 },
{ build: '1.2', bootMs: 1510 },
{ build: '1.3', bootMs: 1290 },
{ build: '1.4', bootMs: 1180 },
]}
/>
Single-series area:
<Chart
type="area"
title="Free heap (KB) over runtime"
xKey="t"
yKey="freeKb"
data={[
{ t: '0s', freeKb: 142 },
{ t: '10s', freeKb: 138 },
{ t: '20s', freeKb: 130 },
{ t: '30s', freeKb: 124 },
{ t: '40s', freeKb: 121 },
{ t: '50s', freeKb: 118 },
]}
/>
Multi-series line:
<Chart
type="line"
title="Monthly sales vs refunds"
xKey="month"
yKey={['sales', 'refunds']}
data={[
{ month: 'Jan', sales: 100, refunds: 8 },
{ month: 'Feb', sales: 120, refunds: 15 },
{ month: 'Mar', sales: 95, refunds: 10 },
{ month: 'Apr', sales: 140, refunds: 12 },
]}
/>
Multi-series bar:
<Chart
type="bar"
title="Pass / fail counts per board"
xKey="board"
yKey={['pass', 'fail']}
data={[
{ board: 'esp32-s3', pass: 42, fail: 3 },
{ board: 'esp32-c6', pass: 38, fail: 7 },
{ board: 'rp2040', pass: 45, fail: 1 },
{ board: 'nrf52840', pass: 40, fail: 5 },
]}
/>
Multi-series area:
<Chart
type="area"
title="CPU vs memory usage (%)"
xKey="t"
yKey={['cpu', 'mem']}
data={[
{ t: '0s', cpu: 12, mem: 30 },
{ t: '5s', cpu: 28, mem: 34 },
{ t: '10s', cpu: 65, mem: 42 },
{ t: '15s', cpu: 71, mem: 51 },
{ t: '20s', cpu: 48, mem: 49 },
{ t: '25s', cpu: 22, mem: 45 },
]}
/>
Pass chart data inline via data={[…]}. Sourcing data from a separate
project file isn't supported yet.
Embedding images from the project
An .mdx file can reference any image stored in the same project — useful
for screenshots, diagrams, or rendered state machines that should always
reflect the latest version in the project.
Reference images with a relative path, just like in a normal markdown file:


The viewer resolves the relative path against the document's location in the project bucket, then loads the image through the project's permission-checked file API. Update the source file and refresh the doc — the embedded image updates with it.
How the path is resolved
The path is resolved relative to the document's own folder, exactly like links in any markdown file — so where you put the image matters:
documents/connectors.mdx ← the doc
documents/connectors-assets/panel.png ← an asset next to it
photos/top.jpg ← a root-level photo
From documents/connectors.mdx | Resolves to |
|---|---|
 | documents/connectors-assets/panel.png |
 | photos/top.jpg |
A common pattern is to keep a doc's images in a sibling subfolder and push them
with --path:
embedhub push assets/ myorg/myproject --path documents/connectors-assets/
embedhub push connectors.mdx myorg/myproject --path documents/
<img>The viewer renders markdown but strips raw HTML, so an <img src="…"> tag
silently disappears — no image, no error. Always use the markdown form
. The same applies to <img> pointing at a data: URI: the tag
is dropped before the URL is ever read.
 <!-- ✅ renders -->
<img src="connectors-assets/panel.png" /> <!-- ❌ stripped, shows nothing -->
/A leading slash (/photos/top.jpg) is treated as a site-root URL, not a
project path — it bypasses the file API and 404s. Use a relative path
(connectors-assets/… or ../photos/…) instead.
Project-relative image paths only resolve in .mdx files. Inside a plain
.md file they're handed straight to the browser, which interprets them
relative to the page URL and fails to load. Use .mdx whenever you embed
images from the project.
Because raw HTML is stripped, there's no width/height attribute to size an
image — markdown images render at their natural pixel size. Resize the source
file before uploading (e.g. convert in.png -resize 150x out.png) to control
how large it appears, especially for thumbnails inside table cells.
Absolute URLs (https://…) work in both .md and .mdx.
Tips
- Links in MDX use
[text](url). MDX treats<as the start of a component tag, so<https://example.com>autolinks don't work in.mdxfiles. They still work in plain.md. - Component names are case-sensitive.
<Note>works;<note>is treated as an unknown HTML element. - Compile errors surface in the viewer. If the MDX is malformed, the modal shows the parser message with a line and column so you can fix the source and re-upload.
Uploading
.md and .mdx files are uploaded the same way as any other file:
embedhub push README.mdx myorg/myproject --path documents/
See the CLI reference for the full set of flags.