Tangly v0.2 ships richer code blocks, page chrome, and more — see what's new

Markdown & MDX

Every Markdown / MDX feature Tangly understands — text, headings, lists, tables, code, links, images, MDX, LaTeX, embeds.

Tangly pages are MDX — Markdown with embedded JSX. The Markdown half is GitHub-flavored CommonMark + a few additions. The JSX half lets you drop a component anywhere a paragraph would go.

This page covers every Markdown / MDX feature Tangly understands. For per-page metadata see Frontmatter; for the JSX components themselves see Components.

Text & headings

Headings

Use ATX-style headings (#, ##, ###, …). Setext headings (===== underlines) work but aren’t recommended — they don’t survive automated edits well.

md
## H2 — section
### H3 — subsection
#### H4 — sub-subsection

The page H1 is rendered automatically from the title frontmatter field. Don’t write your own # Title at the top — you’ll get two H1s.

Auto-generated heading IDs

Every heading gets a slug-style id derived from the heading text. Punctuation strips, spaces become hyphens, casing lowercases. ## Rate limitsid="rate-limits" → linkable as /page#rate-limits.

Explicit IDs

Pin a heading’s id to keep it stable across renames:

md
## Rate limits {#rate-limits}

The marker survives even if you rephrase the heading. Used by <Embed> for cross-page block embeds.

Paragraphs and breaks

Blank lines separate paragraphs. Single newlines are soft breaks (typically rendered as a space). For an explicit hard break, end a line with two spaces or a backslash.

Emphasis

MarkdownRenders
*italic* or _italic_italic
**bold** or __bold__bold
***bold italic***bold italic
~~strikethrough~~strikethrough
`inline code`inline code

Blockquotes

md
> A quote.
>
> Multiple paragraphs work — leading `>` on every line, blank `>` between.

A quote.

Multiple paragraphs work.

For “callout-shaped” content, use the callout components instead.

Horizontal rule

Three or more -, *, or _ on a line by itself.

md
---

(At the start of a file, --- is frontmatter, not a horizontal rule.)

Footnotes

GitHub-flavored footnotes work. The footnote text renders at the bottom of the page with a back-link.

md
Here is a fact.[^1]

[^1]: With a citation.

Lists & tables

Unordered lists

Use -, *, or +. Pick one and stick to it.

md
- First
- Second
  - Nested (two-space indent)
- Third

Ordered lists

md
1. First
2. Second
3. Third

The numbers don’t have to match the order — 1. 1. 1. renders as 1. 2. 3..

Task lists

md
- [ ] Todo
- [x] Done

Definition lists

md
Term
: Definition.

Tables

md
| Field | Type | Required |
|---|---|---|
| `name` | string | yes |
| `enabled` | boolean | no |

Use colons in the divider row for alignment:

md
| Left | Center | Right |
|:---|:---:|---:|
| 1 | 2 | 3 |

Tables don’t support multi-line cells. For complex layouts, drop into HTML or use <Columns>.

Code

Tangly highlights code with Shiki. Every fenced block carries a copy button by default.

Fenced blocks

md
```ts
const greeting = "hello"
```

File titles

Add title="..." after the language hint. Renders a chip above the code block with an icon derived from the extension.

md
```ts title="src/main.ts"
import { Tangly } from "tangly"
```
src/main.ts
ts
import { Tangly } from "tangly"

Line highlighting

{1,3-5} after the language hint highlights specific lines.

md
```ts {2,4-5}
const a = 1
const b = 2
const c = 3
const d = 4
const e = 5
```

Line numbers

showLineNumbers toggles a number gutter. Combine with line highlighting:

md
```ts showLineNumbers {2}
const a = 1
const b = 2
```

Diff syntax

diff as the language renders +/- lines. For a diff that also keeps language highlighting, use Shiki transforms:

md
```ts
const a = 1
const a = 2
const b = 3
```

Inline code

Single backticks: `tangly check`. Use for function names, file paths, env vars, CLI commands, version numbers.

Multi-language tabs

For the same code in multiple languages, wrap in <CodeGroup> — auto-syncs across pages on known label families (npm/yarn/pnpm/bun, macOS/Linux/Windows, etc.).

Theme & copy button

Pick the Shiki theme via code.theme in docs.json:

json
{ "code": { "theme": { "light": "github-light", "dark": "github-dark" } } }

To disable the copy button globally:

json
{ "code": { "copyButton": false } }

Reference page slugs with a leading slash. No .mdx extension.

md
See [Quickstart](/quickstart) for the 60-second tour.
See [API auth](/reference/schema#api) for header configuration.

tangly check validates every internal link at build time. A broken [text](/some/page) fails the build.

md
[Astro](https://astro.build)

External links open in a new tab when they contain a different host than the docs site. Tangly auto-adds target="_blank" rel="noopener noreferrer".

md
See the [docs][1] and the [repo][2].

[1]: https://tangly.dev
[2]: https://github.com/tanglydocs/tangly

Heading slugs are auto-generated. ## Rate limits#rate-limits. For stable anchors that survive heading renames, pin the id:

md
[Skip to rate limits](#rate-limits)
[Cross-page anchor](/billing#rate-limits)

## Rate limits {#rate-limits}

Images

md
![Alt text](/images/diagram.png)

Every inline image is wrapped in <LightboxImage> automatically — click to expand fullscreen.

Image paths

Tangly serves the project root. /images/foo.png resolves to <project>/images/foo.png.

The relative-path rewrite

Mintlify projects often write ![](/images/foo.png) from inside a nested guide. Tangly’s MDX preprocessor rewrites these to absolute paths (/images/foo.png) before the Astro asset pipeline sees them. Without the rewrite, Astro cache-misses on every relative path during HMR.

If you ship a Tangly-native project, use absolute paths from day one — faster, less surprising.

Image optimization

Disabled by default. Tangly turns off @astrojs/mdx’s optimize and sets image.service: noop. Mintlify projects often reference absolute external URLs (CDN-hosted assets) that bypass image pipelines anyway, and the cost of resizing every image isn’t worth it for static docs.

If you want optimization, eject and turn it back on in the resulting astro.config.ts.

Captioning + lightbox opt-out

Use <Frame> for figure-style framing and a caption.

mdx
<Frame caption="The default tang theme.">
  ![](/images/screenshot.png)
</Frame>

To opt out of the lightbox:

mdx
<LightboxImage src="/images/icon.png" lightbox={false} />

Image formats

Whatever your browser supports — png, jpg, webp, avif, gif (animated), svg. Tangly doesn’t transcode.

For SVGs as inline icons, use <Icon> with a Lucide name.

MDX (the JSX in MDX)

Built-in components

Every component listed in Components is auto-injected. No imports.

mdx
<Note>This shows up everywhere — no import.</Note>

<CardGroup cols={2}>
  <Card title="One" icon="circle" />
  <Card title="Two" icon="square" />
</CardGroup>

JSX expressions

Curly braces interpolate JavaScript:

mdx
The current year is {new Date().getFullYear()}.

Expressions run at build time. They don’t have access to your data layer — there’s no SSR.

Importing components

For one-off components that aren’t built-in, import at the top of the page:

mdx
---
title: Custom widget
---

import Widget from "../theme/Widget.astro";

# Custom widget

<Widget data={[1, 2, 3]} />
  • Relative imports work from the page’s location.
  • Absolute imports (@/components/Widget.astro) work after ejecting.
  • Without ejecting, stick to relative paths or shadow into theme/.

Exporting metadata

For derived metadata that depends on JS computation, MDX export const works:

mdx
---
title: Roadmap
---

export const milestones = [
  { date: "2026-Q1", focus: "Phase 1" },
  { date: "2026-Q2", focus: "Phase 2" },
]

# Roadmap

{milestones.map(m => <p key={m.date}>{m.date} — {m.focus}</p>)}

Markdown inside JSX

Markdown inside a JSX block needs blank lines around it:

mdx
<Note>

This paragraph is **bold** because the blank line lets it render as Markdown.

</Note>

Without the blank line, the renderer treats the children as a single inline expression and **bold** ships as literal asterisks.

Limitations

  • No client-side React/Vue/Svelte. Tangly outputs static HTML — JSX is server-only.
  • No import from npm packages outside what Tangly bundles. For custom logic, drop the file in theme/ and shadow.
  • HTML inside MDX is allowed but discouraged — most things have a component (<Frame> over <figure>, <Kbd> over <kbd>).

LaTeX

Tangly renders math via KaTeX. Two syntaxes:

Inline math

Single dollar signs:

md
The capacity factor is $\rho = E_{actual} / E_{max}$.

Display math

Double dollar signs on their own lines:

md
$$
E = mc^2
$$

Mintlify `

` shim Mintlify projects use a wrapper element. Tangly's preprocessor rewrites it to `$$…$$` before MDX parsing — either form works. ```md <latex>E = mc^2

### Common patterns

```md
- Fractions: $\frac{a}{b}$
- Subscripts/superscripts: $x_i^2$
- Greek letters: $\alpha, \beta, \pi$
- Sums:
  $$
  \sum_{i=1}^{n} i^2 = \frac{n(n+1)(2n+1)}{6}
  $$
- Matrices:
  $$
  \begin{pmatrix} a & b \\ c & d \end{pmatrix}
  $$

Escaping

Literal dollar signs need escaping: The price is \$10. Without the backslash the parser tries to open a math expression.

Performance

KaTeX renders at build time, not in the browser. Output is plain HTML + a small CSS bundle (loaded only on pages that use math). No client-side JS needed.

Embeds

Three components handle external or cross-page content:

Use caseComponent
Video (YouTube, Vimeo, mp4)<Video>
Generic iframe with caption<Frame>
Cross-page block reuse<Embed>

YouTube / Vimeo / self-hosted video

<Video> detects the provider from src and rewrites to the privacy-mode embed where applicable. Lazy-loaded — no third-party traffic until the player scrolls into view.

mdx
<Video src="https://www.youtube.com/watch?v=jNQXAC9IVRw" />
<Video src="https://vimeo.com/76979871" />
<Video src="/videos/quickstart.mp4" poster="/videos/poster.jpg" transcript="/transcripts/quickstart.txt" />

Loom / CodePen / CodeSandbox / StackBlitz

Use raw <iframe> inside a <Frame>:

mdx
<Frame caption="Team kickoff, v0.3 cycle">
  <iframe
    src="https://www.loom.com/embed/<id>"
    width="100%"
    height="400"
    frameborder="0"
    allowfullscreen
  />
</Frame>

For consistency across projects, drop a theme/Loom.astro wrapper once and use <Loom id="abc" /> everywhere.

Cross-page block embeds

mdx
<Embed page="guides/billing" block="rate-limits" />

Resolves at build time. See Embedded blocks for the workflow.

Source

↑↓ navigate open esc close