Tabs

One panel at a time out of several, each with its own heading. Different content per tab, which is what separates it from a Segmented.

Usage

When to reach for Tabs, what each of its axes is for, and the decisions it leaves to you.

When to use

Tabs put several panels of different content behind one bar and show one at a time. A tab is a place with its own heading. If the thing underneath is the same content drawn another way, use something else.
  • Facets of one object that each deserve their own panel: a record's Overview, Activity, Permissions and Settings.
  • A settings surface where each group is long enough that stacking them all would need a scroll and a map.
  • A working set the reader opens and closes themselves: documents, queries, terminals. That is the editable-card case.
Declare tabs with items. Tabs.TabPane children are deprecated and warn. Use defaultActiveKey for uncontrolled tabs, or activeKey with onChange to control them.
Use something else when
  • The content is the same content, shown differently. Table against board against calendar, daily against monthly. That is Segmented: a segment is a setting, a tab is a place.
  • Choosing moves the reader somewhere else in the product. Different pages, different objects, a level of the information architecture. That is a Menu, which brings the nesting and the current-page semantics a tab list does not have.
  • More than one panel can be open at once. That is Collapse. Tabs have exactly one active key and cannot have none.
  • The panels are ordered and the reader has to finish one before the next. That is Steps. Tabs say nothing about sequence, so a reader can land on the last one first.

Choosing a type

type sets the look of the bar. Three of the six values are antd's and three are added by the kit.
  • line is the default and the one to reach for: an underline under the active label and nothing else. It sits inside a page or a card without drawing a second box around the content.
  • card gives every tab a box and joins the active one to the panel below it. Use it when the panel needs a visible edge of its own, or when the bar sits on a page background rather than on a container.
  • editable-card is card plus the add trigger and a remove glyph per tab. Only for a set the reader owns.
  • floating, padded and docked are added by the kit. Each is a line bar with a hover highlight: floating highlights the label, padded the padded tab, and docked the whole tab down to the rail, with a thicker indicator.
Pick one per surface and keep it. Labels sit where line puts them, so switching between these types does not move the page, at any size or placement. The difference shows under the cursor.
Tabs are neutral by default. The indicator and the active label use the text color, not the primary color, in light and dark mode. Set color="primary" for antd's primary look, with a tinted hover on floating, padded and docked. A bar copied from Ant Design's docs needs it to match.

Placement

tabPlacement takes top, end, bottom and start. start and end follow the reading direction, so a bar mirrors under dir="rtl" with no work from you.
  • top for almost everything. The bar is read before the panel, which is the order the content wants.
  • start or end when there are more tabs than fit across, or the labels are long enough to wrap. A vertical rail grows downwards for free where a horizontal one runs into the overflow menu.
  • bottom is rare and reads as a footer. It is for a bar attached to the bottom of a viewport-height panel, not for an ordinary page section.
centered centers the tab list inside the bar. It is worth it for a short set on a wide bar and bad on a long one, because the first label stops being where the eye starts.
tabBarExtraContent takes a single node, which lands at the end, or an object with left and right. left and right swap in right-to-left layouts. The left node comes first and the right node last, so treat them as start and end.

Size

size takes small, medium and large, and it applies to the bar rather than to the panel. The panel's type scale is whatever you put in it.
  • medium is the default and the answer for a page. It is the v6 rename of v5's middle, which still typechecks.
  • large is antd's page-header size: a bar that is the main navigation of the screen it sits on.
  • small is the in-Modal and in-Drawer size, and inside a card whose own heading is already doing the work.
small also shrinks the label. The kit sets small tabs in the small font size, where antd only tightens the padding, and it shrinks further in compact mode.

What belongs in a label

A tab's label takes any node, so it needs a rule. It is read out of context, one at a time, by anyone scanning the bar.
  • One or two words, parallel across the set. “Overview, Activity, Members”, not “Overview, Recent activity, People who can see this”.
  • A count rides along as a Badge beside the word rather than as a number in it. A zero count renders nothing unless showZero, so an empty tab reads as a plain word rather than as a “0” chip.
  • One extra per label at most. A word, a count and a status dot turn the bar into a table.
  • Put an icon in the item's icon, not in label. The gap between icon and label only applies to an icon in the icon slot.
  • An Icon sits a little low in a tab. antd lifts the glyph in a tab with a bottom margin, and the kit's Icon resets it. Add mb-[0.2em] to the icon if it has to line up with antd's own.
  • Leave label empty on an icon-only tab. Put the icon and an sr-only name in icon. Any label, even a hidden one, adds a gap after the icon. Use card there too: a line tab has no side padding, so an icon-only line tab is only as wide as its glyph.
  • Do not nest a button or a link inside a label. The tab is already the control. The built-in remove button sits beside the tab's button rather than inside it, which is the pattern to copy if you add a control of your own.

Closable and editable tabs

type="editable-card" turns on the add trigger and a remove glyph per tab. Reach for it only when the set belongs to the reader: open documents, saved queries, comparison slots. A fixed set of facets should not be closable, because closing one leaves the reader with no way back to it.
  • Check the action in onEdit. In onEdit(target, action), target is the tab key for remove and the click event for add.
  • onEdit does not change the selection. If you remove the active tab in a controlled bar, pick the next activeKey yourself in the same handler. An uncontrolled bar repairs itself, clamping to the nearest surviving index.
  • An item's own closable: false pins one tab open, which is the usual way to keep a bar from reaching zero tabs. There is no state for “no tabs”, so decide what the panel shows before the reader closes the last one.
  • hideAdd removes the built-in plus so a control of your own can add tabs, typically a Button in tabBarExtraContent. The remove half of onEdit keeps working, so the two can be split.
  • addIcon and removeIcon come from AntuikitProvider, and your prop wins over them.

Overflow

Tabs never wrap to a second row. When the items outrun the bar, the list scrolls and the rest go into a dropdown, whose glyph is moreIcon.
  • Treat the overflow menu as a safety net, not as a plan. Tabs the reader has to open a menu to find are tabs they will not find. If the set does not fit at an ordinary width, it wants a vertical placement or a Menu.
  • Style the overflow menu through classNames.popup.root. The menu renders outside the tabs, so selectors written on the Tabs do not reach it.
  • more reaches the rest of that dropdown's surface, including trigger and placement, because it is passed to the dropdown.
  • On a mobile device the dropdown is not rendered at all and the bar is scrolled by touch instead. Anything reachable only through the menu is unreachable there.
  • A ConfigProvider tabs.moreIcon wins over the moreIcon prop. antd reads the provider first for that one icon, so set it in one place. The kit's default is on the prop, so either one replaces it.

Lazy rendering

A panel mounts when its tab is first activated and then stays mounted, hidden. Local state, scroll position and anything a panel fetched all survive a switch, which is the behavior a tabbed form wants and a tabbed dashboard usually does not.
  • Leave it alone when the panel holds unsaved input. Unmounting a half-filled form because the reader glanced at another tab is the worst outcome on this page.
  • destroyOnHidden unmounts the inactive panel instead. Reach for it when a panel holds a subscription, a map, a chart that keeps measuring, or a timer: things that cost something while nobody is looking.
  • It works on the Tabs and on an individual item, and the value on the Tabs wins where both are set. Set it per item to destroy the one expensive panel and keep the rest.
  • An item's forceRender is the opposite lever: it mounts a panel before its tab has ever been activated. For a panel that has to be in the DOM to be measured, printed or submitted.
  • destroyInactiveTabPane is the old spelling of destroyOnHidden and warns at runtime, on the Tabs and on an item.
  • Replace the deprecated props. These warn at runtime: use items for Tabs.TabPane, tabPlacement for tabPosition, classNames.popup for popupClassName, indicator.size for indicatorSize, and onTabScroll for onPrevClick and onNextClick.