# Tabs
> Keyboard-navigable tabs for parallel content, with sync groups that follow the reader across pages.
Source: https://shelldocs.shellui.dev/docs/components/tabs
`Tabs` splits parallel content into panels the reader switches between : the same instructions per platform, the same task in two tools. The strip and every panel are rendered on the server and `shelldocs.js` does the switching, so tabs work on static hosts with no Blazor runtime.
## Basic use
Wrap two or more `Tab` elements. `Label` is the text in the strip; the tag body is the panel.
```razor
Runs on a .NET host with a live connection per visitor.
Prerendered HTML served from any static host.
Hot reload on markdown, Razor and C# edits.
```
## Default tab
The first tab is selected unless `DefaultValue` names another one, by `Label` or by `Value`.
```razor
winget install Microsoft.DotNet.SDK.10
brew install --cask dotnet-sdk
sudo apt-get install -y dotnet-sdk-10.0
```
## Sync groups
Give several `Tabs` the same `SyncKey` and picking a tab in one switches all of them. The choice is saved in `localStorage`, so it also applies on other pages and on the next visit.
```razor
Source: GitHub Actions.
Build output directory: publish.
Custom domain via a CNAME file in the output.
Custom domain from the dashboard.
```
Use stable keys (`os`, `host`, `language`). The key names the saved preference, so renaming it resets readers' choice. Sync matches tabs by `Value`, or `Label` when there is no `Value` : give tabs that mean the same thing the same value across groups.
## Markdown in panels
Written as an inline tag in the page, each `Tab` takes a markdown body. Leave a blank line between the tags and any fence.
**Run**
```bash
shelldocs dev --port 5200
```
Watches `.md`, `.razor` and `.cs` files.
**Publish**
```bash
shelldocs build --site-url https://docs.example.com
```
Writes a static site to `./publish`.
For panels that are only code, [`CodeGroup`](https://shelldocs.shellui.dev/docs/components/code-group.md) is the same mechanism with a code-block frame.
## Keyboard and accessibility
The strip is a `role="tablist"`. Each button is a `role="tab"` with `aria-selected` and `aria-controls`, and each panel is a `role="tabpanel"` labelled by its tab. Only the selected tab is in the tab order. With a tab focused:
- **Arrow Left / Arrow Right** select the previous or next tab, wrapping around.
- **Home / End** jump to the first or last tab.
- **Tab** moves on to the content after the strip.
## Props
### Tabs
| Name | Type | Default | Description |
|------|------|---------|-------------|
| `DefaultValue` | `string?` | `first tab` | Label or Value of the tab selected on first render. |
| `SyncKey` | `string?` | `—` | Groups with the same key switch together and remember the choice in localStorage. |
| `Class` | `string?` | `—` | Extra classes on the outer element. |
| `ChildContent` | `RenderFragment?` | `—` | The Tab children. |
### Tab
| Name | Type | Default | Description |
|------|------|---------|-------------|
| `Label` (required) | `string` | `—` | Text shown in the strip. |
| `Value` | `string?` | `Label` | Stable identifier used by DefaultValue and sync. |
| `ChildContent` | `RenderFragment?` | `—` | Panel content. |
## Styling
The parts carry stable classes: `.sd-tabs` (outer element, with `data-tabs-value` set to the selected value), `.sd-tabs-list` (strip), `.sd-tabs-trigger` (each button, `aria-selected="true"` when selected) and `.sd-tabs-panel`.
## Name collisions
If your component library also registers a `Tabs`, `` renders yours. This one stays available as `` and ``.
## See also
- [CodeGroup](https://shelldocs.shellui.dev/docs/components/code-group.md): Tabs with a code-block frame.
- [Deployment](https://shelldocs.shellui.dev/docs/guides/deployment.md): What works on a static host and what needs a server.