Embeds#
Embeds are HTML-like tags in Markdown that expand into static HTML at transform time. Two are enabled by default because they produce plain static markup; everything else is opt-in.
| Embed | Option | Default | Authoring form |
|---|---|---|---|
| GitHub card | embeds.github |
true |
<GitHub repo="owner/name" /> |
| Open Graph link card | embeds.openGraph |
true |
<OgCard url="https://..." /> |
| Package manager tabs | embeds.pm |
false |
<pm>npm install pkg</pm> |
| Twitter/X | embeds.twitter |
false |
<Tweet /> or <XPost /> |
| Bluesky | embeds.bluesky |
false |
<Bluesky /> |
| Spotify | embeds.spotify |
false |
<Spotify url="https://..." /> |
| StackBlitz | embeds.stackBlitz |
false |
<StackBlitz url="https://..." /> |
| WebContainer | embeds.webContainer |
false |
<WebContainer /> |
Tabs and YouTube embeds are not part of the embeds option: they are always
processed in SSG builds and dev preview, with no configuration needed. They are
covered below because they share the same authoring model.
Disable every built-in embed with embeds: false, or configure embeds
individually:
import { oxContent } from "@ox-content/vite-plugin";
export default {
plugins: [
oxContent({
embeds: {
github: { maxSourceLines: 120 },
openGraph: { timeout: 5000 },
pm: { sync: true },
twitter: true,
bluesky: true,
},
}),
],
};
GitHub Cards#
embeds.github renders repository cards and source snippets from the GitHub
API at build time. The output is static HTML — no client-side JavaScript, no
third-party widget script.
A repository card:
<GitHub repo="ubugeeei-prod/ox-content" />
all-in-one markdown toolchain ― fastest, tiniest, framework agnostic, powerful, customizable
A source snippet pinned to a ref and line range:
<GitHub repo="ubugeeei-prod/ox-content" path="README.md" ref="main" loc="1-10" />
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./assets/oxcontent-light.svg">
<source media="(prefers-color-scheme: light)" srcset="./assets/oxcontent-dark.svg">
<img alt="Ox Content logo" src="./assets/oxcontent-dark.svg" height="60">
</picture>
</p>
<p align="center">
<strong>High-performance Markdown toolkit</strong><br>A permalink form is also supported — paste a GitHub blob URL with #L2-L8
line anchors:
<GitHub permalink="https://github.com/owner/repo/blob/abc123/src/index.ts#L2-L8" />
| Option | Default | Purpose |
|---|---|---|
token |
"" |
GitHub API token for rate limits and private repos. |
cache |
true |
Cache API responses in memory. |
cacheTTL |
3600000 |
Cache lifetime in milliseconds. |
maxSourceBytes |
200000 |
Skip files larger than this. |
maxSourceLines |
120 |
Max inline lines when no range is given. |
process.env.GITHUB_TOKEN is picked up automatically when no explicit token
is configured. If a repository or file cannot be fetched during the build —
offline CI, rate limits, an invalid path — the embed renders a fallback link
card instead of failing the build.
Open Graph Cards#
embeds.openGraph fetches a page's Open Graph metadata at build time and
renders a static link card:
<OgCard url="https://vite.dev" />
| Option | Default | Purpose |
|---|---|---|
timeout |
10000 |
Fetch timeout in milliseconds. |
cache |
true |
Cache fetched metadata. |
cacheTTL |
3600000 |
Cache lifetime in milliseconds. |
userAgent |
ox-content-ogp-bot/1.0 ... |
User agent sent to the target. |
Unreachable pages fall back to a plain link card. Requests to localhost, private IP ranges, and non-HTTP(S) schemes are rejected, so Markdown content cannot probe the network the build runs in.
Package Manager Tabs#
embeds.pm expands one npm-style command into an accessible tab group for
npm, pnpm, yarn, bun, and vp (Vite+):
oxContent({
embeds: {
pm: true,
},
});
<pm>npm install -D @ox-content/vite-plugin</pm>
vp install -D @ox-content/vite-pluginpnpm add -D @ox-content/vite-pluginbun add -D @ox-content/vite-pluginnpm install -D @ox-content/vite-pluginyarn add -D @ox-content/vite-pluginThe command is converted natively in Rust — npm install -D becomes
pnpm add -D, yarn add -D, bun add -D, and vp install -D, while
npx <bin> becomes vp exec -- <bin>. The tabs work without client-side
JavaScript; selection uses CSS :has(). Opt in to pm: { sync: true } to
synchronize the selected package manager across every block on the page via
localStorage. See
Package Manager Tabs for the full
conversion table.
Tabs#
Generic tab groups use the same widget as package-manager tabs and are always available in SSG builds and dev preview:
<tabs>
<tab label="macOS">brew install oxc</tab>
<tab label="Linux">apt install oxc</tab>
<tab label="Windows">winget install oxc</tab>
</tabs>
A <tab> without a label attribute falls back to Tab 1, Tab 2, and so
on.
YouTube#
YouTube embeds are always processed in SSG builds and dev preview. The iframe
uses privacy-enhanced mode (youtube-nocookie.com) and lazy loading by
default:
<youtube id="aqz-KE-bpKQ" title="Big Buck Bunny" />
id, url, and href attributes are accepted; youtu.be, watch?v=,
shorts, and embed URL shapes are all recognized.
Twitter/X#
embeds.twitter renders posts as static cards and never loads the third-party
widget script. With twitter: true, the embed is a privacy-conscious link
card:
<XPost url="https://x.com/jack/status/20" />

Use the object form to fetch the post body, author, avatar, and photos at build time and serve them from your own origin:
oxContent({
embeds: {
twitter: {
fetch: true,
lang: "en",
mediaOutputDir: "public/ox-content/twitter",
mediaPublicPath: "/ox-content/twitter",
},
},
});
| Option | Default | Purpose |
|---|---|---|
fetch |
false |
Fetch post content at build time. |
lang |
"en" |
Syndication language and displayed date. |
timeout |
10000 |
Metadata request timeout in milliseconds. |
cache |
true |
In-memory and persistent JSON caches. |
cacheDir |
.cache/ox-content/twitter |
Persistent metadata cache directory. |
mediaOutputDir |
public/ox-content/twitter |
Local directory for avatars and photos. |
mediaPublicPath |
/ox-content/twitter |
URL prefix emitted for downloaded media. |
Downloaded media is served from your site, so a strict img-src 'self' CSP
keeps working. Deleted or private posts fall back to the link-only card
instead of failing the build. See
Twitter/X Embed for details.
Bluesky#
embeds.bluesky renders a static card. The element body provides the text
shown in the card, so no network request is needed at all:
<Bluesky url="https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l">
👋 Bluesky is an open social network
</Bluesky>
👋 Bluesky is an open social network
Spotify#
embeds.spotify renders the official iframe player for tracks, albums,
playlists, episodes, shows, and artists:
<Spotify url="https://open.spotify.com/track/4uLU6hMCjMI75M1A2tKUQC" />
The output is an <iframe> pointing at open.spotify.com/embed/... with lazy
loading. Unlike the static cards above it is a real third-party player, which
is why it stays opt-in.
StackBlitz#
embeds.stackBlitz turns a StackBlitz project URL into a sandboxed iframe
with embed=1 appended:
<StackBlitz url="https://stackblitz.com/edit/vitejs-vite-abc123"></StackBlitz>
WebContainer#
embeds.webContainer emits a lazy placeholder carrying the project source and
cross-origin isolation metadata, for sites that boot
WebContainers on interaction. The placeholder
itself is fully static:
<WebContainer entry="index.html" title="Demo">
npm install
npm run dev
</WebContainer>
index.htmlBoots on interactionnpm install
npm run devSee WebContainer Embed for the isolation requirements.
Related#
- Mermaid Diagrams — diagram fences rendered to static SVG.
- Built-in Features overview