# 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.