Tabs

Pack multiple views into the same space — the reader clicks a tab to switch what's shown, saving vertical room. Best for "the same information in multiple expressions" (multi-language code / raw vs chart / desktop vs mobile vs API).


When to use

  • Multi-language code samples (the same logic in JS / Python / Rust)
  • Multiple views of the same data (raw JSON / table / chart)
  • Multi-platform / environment comparison (macOS / Windows / Linux install steps; iOS / Android screenshots)
  • API request / response comparison (curl / Node.js / Python call style)
  • Peer independent content (no need to switch — show all together) → use Layout grid
  • Strict sequence (step 1 → 2 → 3) → use Stepper
  • Strong contrast (the user must see all options to decide) → use Layout; tabs make non-active items "invisible"

Basic syntax

```tabs
:::tab title=JavaScript
```js
console.log('hello');
```
:::

:::tab title=Python
```py
print('hello')
```
:::

:::tab title=Rust
```rust
println!("hello");
```
:::
```

Renders: a tab bar at the top, the first tab active by default; click another tab to switch the content area. All tabs share the same vertical space.


All options

Block-level: ```tabs

Option Type Default Purpose
default int / string 0 (first one) Index or title of the default-active tab

Tab-level: :::tab

Option Type Default Purpose
title string (required) Text on the tab bar. Keep short (< 20 chars)

Quote title if it has spaces: title="My Tab".


Examples

Example 1: multi-language code

```tabs
:::tab title=JavaScript
```js
async function getUser(id) {
  const r = await fetch(`/api/user/${id}`);
  return r.json();
}
```
:::

:::tab title=Python
```py
import requests
def get_user(id):
    return requests.get(f'/api/user/{id}').json()
```
:::

:::tab title=Go
```go
func GetUser(id string) (*User, error) {
    resp, err := http.Get("/api/user/" + id)
    // ...
}
```
:::
```

Renders: 3 tabs "JavaScript / Python / Go"; click to switch language. Readers see only the language they care about.

Example 2: rich content (callout, lists, etc.)

```tabs
:::tab title=macOS
1. Download `.dmg`
2. Drag to Applications
3. Launch

> [!INFO]
> Apple Silicon and Intel universal binary
:::

:::tab title=Windows
1. Download `.msi`
2. Double-click to install
3. Launch from the Start menu

> [!WARN]
> Requires Windows 10+
:::

:::tab title=Linux
```bash
sudo apt install acme   # Debian/Ubuntu
sudo dnf install acme   # Fedora
```

Or download `.AppImage` and run directly.
:::
```

Renders: install steps for a hypothetical tool ("acme"), one platform per tab. Tab content can hold any markdown — lists, code blocks, callouts, links — not just plain text.

Example 3: same data in 3 views

```tabs
:::tab title=Raw
```json
{"q1": 100, "q2": 130, "q3": 95, "q4": 180}
```
:::

:::tab title=Table
| Quarter | Revenue |
|---|---|
| Q1 | 100 |
| Q2 | 130 |
| Q3 | 95 |
| Q4 | 180 |
:::

:::tab title=Chart
```vega-lite
{
  "data": {"values": [{"q":"Q1","v":100},{"q":"Q2","v":130},{"q":"Q3","v":95},{"q":"Q4","v":180}]},
  "mark": "bar",
  "encoding": {"x": {"field": "q"}, "y": {"field": "v", "type": "quantitative"}}
}
```
:::
```

Renders: the same quarterly revenue data in three views (JSON / table / bar chart). Readers switch on demand — table for precise reading, chart for trends, JSON for copying.

Example 4: default active tab

```tabs default="Production"
:::tab title=Development
Development environment config...
:::

:::tab title=Staging
Staging environment config...
:::

:::tab title=Production
**Production environment config** (default visible because it's the most critical)
:::
```

Renders: "Production" tab is active by default (because of default="Production"), not the first one.


Plain-text fallback behavior

:::tab is a markdown directive container. In readers without directive support:

:::tab title=JavaScript
content
:::

:::tab title=... and ::: show as literals; markdown inside the tab (code blocks, lists, links) renders normally. All tab content expands inline as a sequence — readers see every piece of information, only losing the "pick one" interactivity.

Per-reader breakdown:

Reader Render
Rho Full tabs (top bar + switching)
GitHub web :::tab title=... literal + tab contents inlined
Obsidian Same (unless a directive plugin is installed)
VS Code default preview Same
cat / less Plain text

All tab content stays readable — that's the heart of plain-text fallback. Worst case: all tab contents are stacked together, not an error.


Common pitfalls

1. Forgot title

```tabs
:::tab                ← ❌ Missing title
content
:::
```

→ Tab bar shows blank or "Tab 1". title is required.

2. Title with spaces, no quotes

:::tab title=My Tab     ← ❌ "Tab" gets parsed as the next modifier
:::tab title="My Tab"   ← ✅

3. Too many tabs (> 5)

Tab count UX
1-3 Best
4-5 OK
6-8 Tab bar crowded; mobile may wrap or scroll
> 8 Anti-pattern. Switch to Layout grid + cards showing all, or sidebar nav.

4. Tabs with wildly uneven content depths

```tabs
:::tab title=A
A is very long: 5 paragraphs, 3 lists, nested callout, code block...
:::
:::tab title=B
B is one sentence.
:::
```

→ Switch to B and the area is empty; switch to A and it's packed. Visual jolt. Improvement: pad B to comparable depth; or switch to Layout grid so readers can compare side by side.

5. Missing ::: close

```tabs
:::tab title=A
A content
                ← ❌ Missing :::
:::tab title=B
B content
:::
```

→ The whole tabs block fails to parse. Every :::tab needs a paired ::: to close.

6. Nesting tabs (not recommended)

```tabs
:::tab title=Outer
```tabs       ← ⚠️ Tabs in tabs; UI gets confusing fast
:::tab title=Inner
:::
```
:::
```

→ Technically supported by Nested DSL, but readers struggle to tell outer from inner tab boundaries. Improvement: lift inner tabs out to be peers, or use a stepper (each step contains tabs).

7. interact blocks inside tabs — fine, but watch state

:::tab title=Demo
```interact
slider x range=0..10 step=1 default=5
template stl:
[Selected] -> [{x}]
```
:::

✅ Legal. But switching away and back may reset the slider state to its initial value (reader-dependent). Don't hide a critical demo in a non-default tab.


Tabs vs Stepper vs Layout — how to choose

Scenario Use
Multiple views / languages / versions (reader picks one) Tabs
Strict step sequence (walk through, with Prev/Next) Stepper
Peer comparison (reader must see all together) Layout grid
Time-ordered events (with dates) Timeline

See also

Open in Rho MD →