Skip to content

Refactor TabPanels to compound Tab children pattern#15

Merged
OmgRod merged 2 commits intomainfrom
copilot/fix-tabpanels-component-issue
Mar 25, 2026
Merged

Refactor TabPanels to compound Tab children pattern#15
OmgRod merged 2 commits intomainfrom
copilot/fix-tabpanels-component-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 25, 2026

TabPanels was non-functional in docs because MDX can't serialize JavaScript object arrays with JSX values. The component also only supported plain-string tab content.

Changes

  • components/Tabs.js — Replaced the tabs array prop with a TabPanels + Tab compound component pattern. Tab accepts title (button label) and children (any React/MD content). TabPanels collects children via React.Children.toArray, filters to elements with a title prop, and tracks active tab by index.

  • components/mdx-components.js — Added Tab to named imports from ./Tabs and registered it in mdxComponents.

  • content/guides/components/tabs.mdx — Updated docs, props tables, and live examples to the new API.

New usage:

<TabPanels>
  <Tab title="JavaScript">npm run dev</Tab>
  <Tab title="pnpm">pnpm dev</Tab>
  <Tab title="Bun">bun run dev</Tab>
</TabPanels>
Original prompt

This section details on the original issue you should resolve

<issue_title>[Bug]: TabPanels component not working</issue_title>
<issue_description>### Summary

They don't display at all (at least in its docs)

Steps to reproduce

1. Look at the docs page for TabPanel
2. It isn't there in live examples
3. Either it's not there at all or the component itself is broken.

Expected behavior

It should display correctly.

Additionally the TabPanels should be refactored so that instead it's a TabPanels element and inside are a bunch of Tab components with the parameter of "title" as a string so contents can be proper HTML or MD instead of plain text

Actual behavior

It doesn't display at all.

Environment

Any

Checklist

  • I searched existing issues for duplicates.
  • I can reproduce this on the latest version.</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

Copilot AI changed the title [WIP] Fix TabPanels component not displaying correctly Refactor TabPanels to compound Tab children pattern Mar 25, 2026
Copilot AI requested a review from OmgRod March 25, 2026 16:01
@OmgRod OmgRod marked this pull request as ready for review March 25, 2026 16:44
Copilot AI review requested due to automatic review settings March 25, 2026 16:44
@OmgRod OmgRod merged commit 659dd15 into main Mar 25, 2026
3 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors the documentation tab component API to work reliably in MDX by moving from a tabs array prop (with JSX values) to a compound-component pattern (<TabPanels><Tab ... /></TabPanels>), enabling rich MD/HTML tab content.

Changes:

  • Replaced tabs array prop usage with TabPanels + Tab compound children API.
  • Registered Tab in the MDX component map so it can be used directly in MDX docs pages.
  • Updated the tabs documentation page (props + examples) to the new API.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
components/Tabs.js Implements compound TabPanels/Tab API and index-based active tab state.
components/mdx-components.js Exposes Tab to MDX by importing/exporting it in the MDX components map.
content/guides/components/tabs.mdx Updates docs and examples to use the new compound API.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread components/Tabs.js
Comment on lines +7 to 12
export function TabPanels({ children }) {
const tabs = React.Children.toArray(children).filter(
(child) => React.isValidElement(child) && child.props.title != null,
);
const [activeIndex, setActiveIndex] = useState(0);

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TabPanels derives tabs from children every render, but activeIndex is initialized once and can become out-of-range if the set/order of children changes (e.g., conditional rendering). That can leave no tab selected and show an empty panel. Consider syncing/clamping activeIndex whenever the computed tabs.length changes (or when the active tab is removed).

Copilot uses AI. Check for mistakes.
Comment thread components/Tabs.js
{tabs.map((tab, index) => (
<button
key={tab.id}
key={tab.props.title}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key={tab.props.title} is not guaranteed to be unique/stable (duplicate titles will cause React key collisions and incorrect reuse). Prefer using the element’s existing key (from React.Children.toArray) or another unique identifier rather than the title text.

Suggested change
key={tab.props.title}
key={tab.key ?? index}

Copilot uses AI. Check for mistakes.
Comment thread components/Tabs.js
@@ -1,8 +1,14 @@
import { useState } from 'react';
import React, { useState } from 'react';
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file now uses a default React import mainly for React.Children/React.isValidElement, but other components in this repo typically import named APIs from react (e.g., components/Accordion.js:1). To stay consistent and enable tree-shaking, consider importing Children/isValidElement/useState as named imports instead of import React, ....

Copilot uses AI. Check for mistakes.
Comment thread components/Tabs.js
Comment on lines 18 to 33
<div className="my-6 overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900">
<div className="flex flex-wrap gap-1 border-b border-slate-200 p-2 dark:border-slate-800">
{tabs.map((tab) => (
{tabs.map((tab, index) => (
<button
key={tab.id}
key={tab.props.title}
type="button"
onClick={() => setActive(tab.id)}
onClick={() => setActiveIndex(index)}
className={`rounded-md px-3 py-1.5 text-sm transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 ${
tab.id === active
index === activeIndex
? 'bg-blue-600 text-white'
: 'text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-800'
}`}
>
{tab.label}
{tab.props.title}
</button>
))}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tab UI is rendered as a set of buttons without tab semantics. For accessibility, consider adding role="tablist" on the container, role="tab" + aria-selected on each button, and role="tabpanel" on the content region (with proper id/aria-controls wiring) so screen readers can interpret this as a tab interface.

Copilot uses AI. Check for mistakes.
@OmgRod OmgRod deleted the copilot/fix-tabpanels-component-issue branch March 25, 2026 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: TabPanels component not working

3 participants