[FEATURE] A tab group element for plugin panes, drawn as a native tab control on desktop

Status Open
Reported on v2.1.293
Maintainer reply None cached
Activity 1 comment · opened Oct 9, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

A plugin Pane cannot draw a tab group that works well in the desktop app.

My plugin's pane has three tabs, each with a name and a count: "Needs you 7", "Findings 4", "PRs 1". One tab is shown at a time. In the terminal I draw them from Box and Text, with the shown tab raised, and a Button per tab takes the click.

In the desktop app's Code mode, the same approach does not hold up:

  • Only a Button takes a click. A Box can have a background and a hover background, but a click on it does nothing.
  • A Button sizes to its label: one line tall, as wide as its text. It does not stretch to fill its parent, and ButtonProps has no width, height or flex props.
  • A Button always draws as a native button, plain or not, so it cannot be an invisible hit area over a drawn tab.

So a desktop tab is either a small native button or a drawn panel where only the label's text takes a click. Neither looks or behaves like a tab group.

Proposed Solution

A Tabs element that the surface draws as its own tab control: a native tab bar or segmented control on desktop, and a tab line in the terminal.

<Tabs
  key="inbox-tabs"
  selected={tab}
  tabs={[
    { id: 'needsYou', label: 'Needs you', count: 7 },
    { id: 'findings', label: 'Findings', count: 4 },
    { id: 'prs', label: 'PRs', count: 1 },
  ]}
  onSelect={id => showTab(id)}
/>
  • Each tab takes a click over its whole shape, with the surface's own hover, focus and selected states.
  • A press raises ui.press (or ui.select) with the tab's id, like other elements.
  • The tab group keeps the focus when the selection changes, so the next click works. Today a pane can lose the focus when the clicked element leaves the drawing (#100874).
  • An optional count or badge per tab, drawn in the surface's style.
  • In the terminal, the arrow keys or a digit could move between tabs, as the surface decides.

Alternative Solutions

  • Layout props on Button: width, height or flexGrow, and padding. A plugin could then size a button to a drawn tab. This is more general, but each plugin would rebuild the tab group and its selected state by hand.
  • A pressable Box: onPress on Box would let a plugin draw any shape and take clicks over all of it.
  • What I do today: each tab is a native Button, with the shown tab as variant="primary". The labels are padded with non-breaking spaces to widen the hit area. Plain spaces draw with no size.
  • What I tried first: blank plain Buttons laid over a drawn tab panel. They sized to their labels instead of the panel and showed native highlights under the pointer, so the tab looked like stacked buttons.

Priority

Medium - Would be very helpful

Feature Category

Developer tools/SDK

Use Case Example

  1. A plugin keeps a pane open beside the conversation, with tabs for questions, findings and pull requests.
  2. In the desktop app, the person clicks "PRs 1" to see the one PR that needs them.
  3. With a tab group element, the whole tab takes the click, shows its hover and selected states, and keeps the pane's focus for the next click.
  4. The plugin draws one element for every surface, instead of a terminal drawing and a desktop workaround.

Additional Context

  • Claude desktop app 2.31226.0, which bundles Claude Code 2.1.293, on macOS 15.7.3.
  • Button children (Text inside a Button) arrive in 2.1.295 and would let the count keep its color. They do not change the button's size.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗