base-ui icon indicating copy to clipboard operation
base-ui copied to clipboard

[accordion][tabs] Make `value` a required prop

Open michaldudak opened this issue 5 months ago • 12 comments

Made value in Accordion.Item, Tabs.Tab, and Tabs.Panel a mandatory prop. This helps to solve several issues with these components:

  • closes #1880
  • closes #1588
  • closes #2096

michaldudak avatar Jun 17 '25 09:06 michaldudak

Open in StackBlitz

npm i https://pkg.pr.new/@base-ui-components/react@2124

commit: f91f634

pkg-pr-new[bot] avatar Jun 17 '25 09:06 pkg-pr-new[bot]

Bundle size report

@base-ui-components/reactparsed: ▼-523B(-0.17%) gzip: ▼-149B(-0.15%)

Show details for 39 more bundles

@base-ui-components/react/tabsparsed: ▼-521B(-2.01%) gzip: ▼-153B(-1.64%) @base-ui-components/react/accordionparsed: ▼-7B(-0.03%) gzip: 0B(0.00%) @base-ui-components/react/alert-dialogparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/avatarparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/checkboxparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/checkbox-groupparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/collapsibleparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/context-menuparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/dialogparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/direction-providerparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/fieldparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/fieldsetparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/formparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/inputparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/menuparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/menubarparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/merge-propsparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/meterparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/navigation-menuparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/number-fieldparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/popoverparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/preview-cardparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/progressparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/radioparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/radio-groupparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/scroll-areaparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/selectparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/separatorparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/sliderparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/switchparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/toastparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/toggleparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/toggle-groupparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/toolbarparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/tooltipparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/unstable-no-ssrparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/unstable-use-media-queryparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/use-renderparsed: 0B(0.00%) gzip: 0B(0.00%) @base-ui-components/react/utilsparsed: 0B(0.00%) gzip: 0B(0.00%)

Details of bundle changes

Generated by :no_entry_sign: dangerJS against f91f6340a4f0ce0e4a87c002a26ed46a2f9576fe

mui-bot avatar Jun 17 '25 09:06 mui-bot

Deploy Preview for base-ui ready!

Name Link
Latest commit f91f6340a4f0ce0e4a87c002a26ed46a2f9576fe
Latest deploy log https://app.netlify.com/projects/base-ui/deploys/68556063d05a530008494aaa
Deploy Preview https://deploy-preview-2124--base-ui.netlify.app
Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

netlify[bot] avatar Jun 17 '25 09:06 netlify[bot]

Would be good to add tests for each of those cases

atomiks avatar Jun 17 '25 10:06 atomiks

Updated.

michaldudak avatar Jun 18 '25 09:06 michaldudak

NavigationMenu uses useId() inside of its Item component as a fallback when value is missing. I wonder if something similar could be used without requiring value

https://github.com/mui/base-ui/blob/8ba89eadb183833239310328ccf98af2953e17d5/packages/react/src/navigation-menu/item/NavigationMenuItem.tsx#L20-L21

atomiks avatar Jun 18 '25 13:06 atomiks

useBaseUiId can be undefined before effects run (in React 17) and we need to have this value defined during SSR to get the tab panel content in the prerendered response. NavigationMenu doesn't need to be rendered on the server, so useBaseUiId is sufficient.

michaldudak avatar Jun 18 '25 16:06 michaldudak

useBaseUiId can be undefined before effects run (in React 17) and we need to have this value defined during SSR to get the tab panel content in the prerendered response.

So it's only a problem on React 17? I don't like that we need to change to a worse API to support a version of React that's fast becoming irrelevant; it's not future-looking. I'd rather change the type to be optional and just throw a runtime warning for React 17.

atomiks avatar Jun 18 '25 16:06 atomiks

With useId is it possible to "fully" unmount the TabPanel when keepMounted={false}? https://github.com/mui/base-ui/issues/1872

mj12albert avatar Jun 18 '25 18:06 mj12albert

It's not just that. The value must be possible to be figured out by developer so it can be used in defaultValue or value props. There's no way to do it with useId - the only sensible options are explicit value or index.

This approach is possible in NavigationMenu as defaultValue will often be null. In Tabs it's not common to see no tabs selected. Even in NavigationMenu you should provide explicit values if you want to control its state from the outside, right?

michaldudak avatar Jun 20 '25 13:06 michaldudak

It's not just that. The value must be possible to be figured out by developer so it can be used in defaultValue or value props. There's no way to do it with useId - the only sensible options are explicit value or index.

Yeah but imo it seems intuitive/expected that the value prop becomes required when you need to control it or select an initial tab. For Accordion this would also be less common than Tabs I imagine.

This approach is possible in NavigationMenu as defaultValue will often be null. In Tabs it's not common to see no tabs selected. Even in NavigationMenu you should provide explicit values if you want to control its state from the outside, right?

Yes, though it seems like with Tabs defaulting to the first tab is ok in many cases and you don't need to control it?

atomiks avatar Jun 23 '25 22:06 atomiks

I think that defaulting to the first (non disabled) tab being selected feels reasonable, if no value (or defaultValue) are passed.

ciampo avatar Jun 24 '25 17:06 ciampo

Closed in #3373

michaldudak avatar Dec 04 '25 19:12 michaldudak