Skip to content

[combobox] New Combobox and Autocomplete components#2105

Merged
atomiks merged 276 commits into
mui:masterfrom
atomiks:feat/Combobox
Sep 3, 2025
Merged

[combobox] New Combobox and Autocomplete components#2105
atomiks merged 276 commits into
mui:masterfrom
atomiks:feat/Combobox

Conversation

@atomiks

@atomiks atomiks commented Jun 12, 2025

Copy link
Copy Markdown
Contributor

@atomiks atomiks added component: combobox type: new feature Expand the scope of the product to solve a new problem. labels Jun 12, 2025
@pkg-pr-new

pkg-pr-new Bot commented Jun 12, 2025

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/mui/base-ui/@base-ui-components/react@2105
pnpm add https://pkg.pr.new/mui/base-ui/@base-ui-components/utils@2105

commit: 88e36a7

@mui-bot

mui-bot commented Jun 12, 2025

Copy link
Copy Markdown

Bundle size report

Total Size Change: 🔺+301KB(+20.91%) - Total Gzip Change: 🔺+103KB(+20.17%)
Files: 71 total (2 added, 0 removed, 19 changed)

Show details for 71 more bundles

@base-ui-components/reactparsed: 🔺+36.7KB(+11.79%) gzip: 🔺+10.6KB(+10.66%)
@base-ui-components/react/navigation-menuparsed: 🔺+1.76KB(+1.96%) gzip: 🔺+731B(+2.33%)
@base-ui-components/react/menuparsed: 🔺+1.68KB(+1.48%) gzip: 🔺+660B(+1.68%)
@base-ui-components/react/selectparsed: 🔺+1.67KB(+1.42%) gzip: 🔺+658B(+1.61%)
@base-ui-components/react/context-menuparsed: 🔺+1.67KB(+1.48%) gzip: 🔺+668B(+1.71%)
@base-ui-components/react/tabsparsed: 🔺+808B(+3.09%) gzip: 🔺+326B(+3.46%)
@base-ui-components/react/toolbarparsed: 🔺+806B(+4.12%) gzip: 🔺+344B(+4.92%)
@base-ui-components/react/popoverparsed: 🔺+776B(+0.91%) gzip: 🔺+319B(+1.06%)
@base-ui-components/react/radio-groupparsed: 🔺+753B(+3.55%) gzip: 🔺+324B(+4.03%)
@base-ui-components/react/menubarparsed: 🔺+751B(+3.47%) gzip: 🔺+325B(+4.08%)
@base-ui-components/react/toggle-groupparsed: 🔺+751B(+5.03%) gzip: 🔺+331B(+5.83%)
@base-ui-components/react/preview-cardparsed: 🔺+489B(+0.85%) gzip: 🔺+212B(+1.03%)
@base-ui-components/react/tooltipparsed: 🔺+482B(+0.76%) gzip: 🔺+202B(+0.90%)
@base-ui-components/react/alert-dialogparsed: 🔺+287B(+0.57%) gzip: 🔺+89B(+0.50%)
@base-ui-components/react/dialogparsed: 🔺+286B(+0.56%) gzip: 🔺+100B(+0.56%)
@base-ui-components/react/accordionparsed: 🔺+55B(+0.25%) gzip: 🔺+15B(+0.19%)
@base-ui-components/react/radioparsed: 🔺+55B(+0.38%) gzip: 🔺+5B(+0.09%)
@base-ui-components/react/sliderparsed: 🔺+55B(+0.21%) gzip: 🔺+27B(+0.27%)
@base-ui-components/react/toggleparsed: 🔺+55B(+0.64%) gzip: 🔺+14B(+0.39%)
@base-ui-components/react/comboboxparsed: 🔺+128KB(new) gzip: 🔺+43.8KB(new)
@base-ui-components/react/autocompleteparsed: 🔺+124KB(new) gzip: 🔺+42.9KB(new)
@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/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/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/number-fieldparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/react/progressparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/react/scroll-areaparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/react/separatorparsed: 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/typesparsed: 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/utils/detectBrowserparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/errorparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/fastObjectShallowCompareparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/generateIdparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/getReactElementRefparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/inertValueparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/isElementDisabledparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/isMouseWithinBoundsparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/mergeObjectsparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/ownerparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/reactVersionparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/safeReactparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/storeparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useAnimationFrameparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useControlledparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useEnhancedClickHandlerparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useEventCallbackparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useForcedRerenderingparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useIdparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useIntervalparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useIsoLayoutEffectparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useLatestRefparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useMergedRefsparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useOnFirstRenderparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useOnMountparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useRefWithInitparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/useTimeoutparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/visuallyHiddenparsed: 0B(0.00%) gzip: 0B(0.00%)
@base-ui-components/utils/warnparsed: 0B(0.00%) gzip: 0B(0.00%)

Details of bundle changes

Generated by 🚫 dangerJS against 88e36a7

@netlify

netlify Bot commented Jun 12, 2025

Copy link
Copy Markdown

Deploy Preview for base-ui ready!

Name Link
🔨 Latest commit 88e36a7
🔍 Latest deploy log https://app.netlify.com/projects/base-ui/deploys/68b851b9d2f27d0008545e3a
😎 Deploy Preview https://deploy-preview-2105--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.

@atomiks
atomiks force-pushed the feat/Combobox branch 4 times, most recently from 41a4d0f to 04f2b69 Compare June 12, 2025 12:57
@michael-land

Copy link
Copy Markdown

it would be really nice this component will also support autoHighlight, similar to MUI Autocomplete.

@atomiks

This comment was marked as outdated.

@atomiks
atomiks force-pushed the feat/Combobox branch 3 times, most recently from a851184 to 508c8de Compare June 17, 2025 02:23
@github-actions github-actions Bot added PR: out-of-date The pull request has merge conflicts and can't be merged. and removed PR: out-of-date The pull request has merge conflicts and can't be merged. labels Jun 17, 2025
@atomiks
atomiks force-pushed the feat/Combobox branch 6 times, most recently from 5de6915 to 27f8e3e Compare June 23, 2025 07:29
@github-actions github-actions Bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Jun 23, 2025
@github-actions github-actions Bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Jun 24, 2025
@atomiks
atomiks force-pushed the feat/Combobox branch 2 times, most recently from 586ccbc to ca828da Compare June 24, 2025 06:36

@michaldudak michaldudak left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I haven't found any serious issues and the API looks solid. But there are many potential use cases we might not considered, so let's release this and see how the community feels about it. In case there's something fundamentally wrong (which I doubt), we still have time to fix before v1.

@mui-bot

mui-bot commented Sep 3, 2025

Copy link
Copy Markdown

Bundle size report

Bundle Parsed size Gzip size
@base-ui-components/react 🔺+36.7KB(+11.79%) 🔺+10.6KB(+10.66%)

Details of bundle changes

@LukasTy LukasTy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The components look amazing! 😍
That's a massive undertaking, awesome work. 💯 🚀
Sorry for a late review. 🙈
I will check a11y behavior next.


I have a behavioral observation:
The Autocomplete closes the items after searching and removing search query.
Is that the expected behavior? Or should it keep open with unfiltered items, just like before opening it?

Screen.Recording.2025-09-02.at.13.51.58.mov

year: number;
}

const top100Movies: Movie[] = [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

General nitpick: Is there a way to share this data between CSS Modules and Tailwind examples? 🤔

);

const input = screen.getByRole<HTMLInputElement>('combobox');
fireEvent.click(input);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nitpick

Suggested change
fireEvent.click(input);
await user.click(input);

Comment thread packages/react/src/autocomplete/root/AutocompleteRoot.test.tsx

expect(input).not.to.have.attribute('data-focused');

fireEvent.focus(input);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nitpick: user.keyboard('{Tab}') would probably work as well. 🤔

Comment thread packages/react/src/autocomplete/root/AutocompleteRoot.test.tsx Outdated
Comment thread packages/react/src/combobox/item/ComboboxItem.tsx Outdated
Comment thread packages/react/src/combobox/positioner/ComboboxPositioner.tsx Outdated
Comment thread packages/react/src/combobox/trigger/ComboboxTrigger.tsx Outdated
Comment thread packages/react/src/combobox/root/ComboboxRootInternal.tsx
Comment thread packages/react/src/combobox/root/ComboboxRootInternal.tsx
Comment thread packages/react/src/combobox/clear/ComboboxClear.tsx Outdated
Comment thread packages/react/src/combobox/item/ComboboxItem.tsx Outdated
Comment thread packages/react/src/combobox/trigger/ComboboxTrigger.tsx Outdated

@mnajdova mnajdova left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🎉 Well done James!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: autocomplete Changes related to the autocomplete component. component: combobox Changes related to the combobox component. type: new feature Expand the scope of the product to solve a new problem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[combobox] Implement Combobox