Skip to content

Conversation

@atomiks
Copy link
Contributor

@atomiks atomiks commented May 13, 2025

Fixes #1843
Fixes #2117

Adds nativeButton: boolean prop to components that use useButton.

When using the render prop, you must specify nativeButton={boolean} if the tag that's rendered differs from the default one (these are documented already as Renders a <button> element or Renders a <div> element).

@pkg-pr-new
Copy link

pkg-pr-new bot commented May 13, 2025

Open in StackBlitz

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

commit: 5377f88

@netlify
Copy link

netlify bot commented May 13, 2025

Deploy Preview for base-ui ready!

Name Link
🔨 Latest commit 5377f88
🔍 Latest deploy log https://app.netlify.com/projects/base-ui/deploys/683ec4e5868cdd00081b95a4
😎 Deploy Preview https://deploy-preview-1909--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.

@@ -1,7 +1,5 @@
'use client';
Copy link
Member

@mj12albert mj12albert May 21, 2025

Choose a reason for hiding this comment

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

I think this hook was for in case we ever wanted to expose the element/tag name as a prop for other components, since we're not doing that it can just be dropped and merged into useButton

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The event handler still uses it inside the events for e.g. event.preventDefault()

@michaldudak
Copy link
Member

We don't really need to know the actual rendered tag name, but just the fact that they want to render something different than the native <button />. So a boolean prop would suffice.

@atomiks
Copy link
Contributor Author

atomiks commented May 27, 2025

@michaldudak it also checks for a when computing the rendered props. It will avoid role="button" in that case

} else if (elementName !== '') {
if (elementName !== 'A') {

@michaldudak
Copy link
Member

michaldudak commented May 27, 2025

I don't think we should treat links the same way as buttons. I'd rather have separate useLink hook. This code was ported over from Material UI, where it may make more sense to have a more generic notion of a button, but we are low-level enough to separate buttons and links.

@atomiks
Copy link
Contributor Author

atomiks commented May 27, 2025

Seems fair enough. Our components usually have a role="" specified (like menuitem or option) to begin with so it might not be an issue. I'll see what I can change.

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label May 27, 2025
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label May 27, 2025
@atomiks atomiks changed the title [useButton] Ensure disabled attribute is present during SSR [useButton] nativeButton prop May 27, 2025
@atomiks
Copy link
Contributor Author

atomiks commented May 27, 2025

@michaldudak updated PR description

Adds nativeButton: boolean to any component that uses useButton. We could check the simple case (a plain element like <div />) or always require it to be specified if it differs from the default rendered tag (probably better as it's more explicit/less magical/more consistent).

@atomiks atomiks requested a review from mj12albert May 27, 2025 11:03
@atomiks atomiks force-pushed the fix/use-button-disabled-ssr branch from 99db41e to 3a4f135 Compare May 27, 2025 11:05
@atomiks atomiks force-pushed the fix/use-button-disabled-ssr branch from 3a4f135 to 2091c45 Compare May 27, 2025 11:05
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label May 30, 2025
@mui-bot
Copy link

mui-bot commented May 30, 2025

Bundle size report

Total Size Change:${\tiny{\color{green}▼}}$-5.56KB(-0.39%) - Total Gzip Change:${\tiny{\color{green}▼}}$-1.53KB(-0.31%)
Files: 41 total (0 added, 0 removed, 18 changed)

@base-ui-components/reactparsed:${\tiny{\color{red}▲}}$+382B(+0.13%) gzip:${\tiny{\color{red}▲}}$+147B(+0.16%)
@base-ui-components/react/switchparsed:${\tiny{\color{green}▼}}$-415B(-2.91%) gzip:${\tiny{\color{green}▼}}$-125B(-2.27%)
@base-ui-components/react/radioparsed:${\tiny{\color{green}▼}}$-413B(-2.75%) gzip:${\tiny{\color{green}▼}}$-135B(-2.37%)
@base-ui-components/react/number-fieldparsed:${\tiny{\color{green}▼}}$-409B(-1.42%) gzip:${\tiny{\color{green}▼}}$-121B(-1.17%)
@base-ui-components/react/toolbarparsed:${\tiny{\color{green}▼}}$-404B(-2.18%) gzip:${\tiny{\color{green}▼}}$-62B(-0.94%)
@base-ui-components/react/checkboxparsed:${\tiny{\color{green}▼}}$-387B(-2.12%) gzip:${\tiny{\color{green}▼}}$-100B(-1.46%)
Base UI checkboxparsed:${\tiny{\color{green}▼}}$-387B(-2.12%) gzip:${\tiny{\color{green}▼}}$-101B(-1.47%)
@base-ui-components/react/tabsparsed:${\tiny{\color{green}▼}}$-386B(-1.60%) gzip:${\tiny{\color{green}▼}}$-106B(-1.23%)
@base-ui-components/react/popoverparsed:${\tiny{\color{green}▼}}$-383B(-0.44%) gzip:${\tiny{\color{green}▼}}$-110B(-0.38%)
@base-ui-components/react/accordionparsed:${\tiny{\color{green}▼}}$-382B(-1.65%) gzip:${\tiny{\color{green}▼}}$-108B(-1.35%)

Show 8 more bundle changes

@base-ui-components/react/collapsibleparsed:${\tiny{\color{green}▼}}$-382B(-2.11%) gzip:${\tiny{\color{green}▼}}$-113B(-1.80%)
@base-ui-components/react/toggleparsed:${\tiny{\color{green}▼}}$-382B(-4.25%) gzip:${\tiny{\color{green}▼}}$-109B(-2.98%)
@base-ui-components/react/toastparsed:${\tiny{\color{green}▼}}$-355B(-1.34%) gzip:${\tiny{\color{green}▼}}$-107B(-1.12%)
@base-ui-components/react/alert-dialogparsed:${\tiny{\color{green}▼}}$-326B(-0.63%) gzip:${\tiny{\color{green}▼}}$-86B(-0.50%)
@base-ui-components/react/dialogparsed:${\tiny{\color{green}▼}}$-326B(-0.63%) gzip:${\tiny{\color{green}▼}}$-87B(-0.50%)
@base-ui-components/react/selectparsed:${\tiny{\color{green}▼}}$-266B(-0.24%) gzip:${\tiny{\color{green}▼}}$-84B(-0.23%)
@base-ui-components/react/context-menuparsed:${\tiny{\color{green}▼}}$-197B(-0.18%) gzip:${\tiny{\color{green}▼}}$-75B(-0.20%)
@base-ui-components/react/menuparsed:${\tiny{\color{green}▼}}$-137B(-0.12%) gzip:${\tiny{\color{green}▼}}$-51B(-0.14%)

Details of bundle changes

Generated by 🚫 dangerJS against 5377f88

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Jun 2, 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 3, 2025
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Jun 3, 2025
buttonRef: forwardedRef,
elementName: 'a',
native: 'a',
focusableWhenDisabled: true,
Copy link
Member

Choose a reason for hiding this comment

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

Links cannot be disabled, thats why focusableWhenDisabled wasn't passed here; I realize the TOOLBAR_LINK_METADATA above is super misleading, but the true is only used for deriving disabledIndices

I added a comment about this here, sorry for conflicting this PR again btw 🙏

Copy link
Member

Choose a reason for hiding this comment

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

I'd also remove support for links in useButton (could be in a separate PR) and create another hook if necessary.

Copy link
Member

Choose a reason for hiding this comment

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

I agree to do it separately; at a glance there's actually nothing to put in a useLink hook and we can just remove the link logic

ToolbarLink is the only link component using this; the only other link component we have is NavigationMenu.Link and that doesn't use useButton

buttonRef: forwardedRef,
elementName: 'a',
native: 'a',
focusableWhenDisabled: true,
Copy link
Member

Choose a reason for hiding this comment

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

I'd also remove support for links in useButton (could be in a separate PR) and create another hook if necessary.

"nativeButton": {
"type": "boolean",
"default": "true",
"description": "Determines whether the component is being rendered as a native button."
Copy link
Member

Choose a reason for hiding this comment

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

IMO we need a more detailed description what it's for. How about adding "Set it to false if the render prop renders a non-interactive element, like div or span."?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It can technically be replaced with an interactive element, so I think any different tag should be the doc

@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Jun 3, 2025
Comment on lines 36 to 41
if (hasError) {
throw new Error(
`Set the \`nativeButton\` prop to \`${isButtonButFalse}\` when rendering a <${tagNameLower}> element.`,
);
}
}, [native]);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since this prop is quite obscure and not shown in the types when using render by default, a runtime error is likely required

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nevermind, doesn't play nicely with the conformance tests

@atomiks atomiks merged commit 9387240 into mui:master Jun 4, 2025
22 checks passed
@atomiks atomiks deleted the fix/use-button-disabled-ssr branch June 4, 2025 02:17
@mj12albert mj12albert added the breaking change Introduces changes that are not backward compatible. label Jul 16, 2025
@oliviertassinari oliviertassinari added internal Behind-the-scenes enhancement. Formerly called “core”. and removed core labels Aug 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Introduces changes that are not backward compatible. internal Behind-the-scenes enhancement. Formerly called “core”.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[form] Form Fails to Submit via Enter Key When a Checkbox is Present [menu] Disabled button/trigger is missing disabled attribute during SSR

5 participants