Bug report
Current Behavior
When using a <button type="submit" form="form-id"> inside a Radix component with asChild, native form submission via the HTML form attribute doesn't work. The dropdown closes but the form is not submitted.
Adding an explicit onClick handler (even a no-op onClick={() => {}}) to the button makes native form submission work correctly.
Expected behavior
A submit button with a form attribute should trigger native form submission when clicked, regardless of whether an explicit onClick handler is provided.
Reproducible example
import { DropdownMenu } from '@radix-ui/react-dropdown-menu';
function Example() {
return (
<>
<DropdownMenu.Root>
<DropdownMenu.Trigger>Open</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item asChild>
{/* Does NOT submit the form */}
<button type="submit" form="my-form">Submit</button>
{/* Adding onClick={() => {}} DOES submit the form */}
{/* <button type="submit" form="my-form" onClick={() => {}}>Submit</button> */}
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
<form id="my-form" action="/submit" method="post" />
</>
);
}
Suggested solution
The issue appears to be in packages/react/slot/src/slot.tsx in the mergeProps function. Event handlers are only composed when both parent and child have them:
if (isHandler) {
if (slotPropValue && childPropValue) {
// Both exist: composed - native form submission WORKS
overrideProps[propName] = (...args) => {
childPropValue(...args);
slotPropValue(...args);
};
} else if (slotPropValue) {
// Only slot has handler - native form submission BROKEN
overrideProps[propName] = slotPropValue;
}
}
When only the slot's handler exists, native browser behavior is somehow prevented. When handlers are composed, native behavior is preserved.
Note: A wrapper component workaround does not work:
const FormButton = forwardRef(({ onClick, ...props }, ref) => (
<button ref={ref} onClick={onClick ?? (() => {})} {...props} />
));
// This does NOT work - Slot merges with FormButton's props, not the rendered button's props
<DropdownMenu.Item asChild>
<FormButton type="submit" form="my-form">Submit</FormButton>
</DropdownMenu.Item>
Because Slot uses children.props (the immediate child element's props), not the rendered output's props, the internal onClick never participates in the merge.
The fix may involve ensuring the slot-only handler path doesn't interfere with native form submission, or always wrapping handlers in a way that preserves native behavior.
Additional context
This affects any use case where a native HTML form button is used inside a Radix component with asChild, particularly when the form element is outside the dropdown (to avoid DOM disconnection when the dropdown closes).
Your environment
| Software |
Name(s) |
Version |
| Radix Package(s) |
@radix-ui/react-slot, @radix-ui/react-dropdown-menu |
|
| React |
n/a |
19 |
| Browser |
Chrome |
|
| Assistive tech |
|
|
| Node |
n/a |
22 |
| npm/yarn/pnpm |
npm |
|
| Operating System |
macOS |
|
Bug report
Current Behavior
When using a
<button type="submit" form="form-id">inside a Radix component withasChild, native form submission via the HTMLformattribute doesn't work. The dropdown closes but the form is not submitted.Adding an explicit
onClickhandler (even a no-oponClick={() => {}}) to the button makes native form submission work correctly.Expected behavior
A submit button with a
formattribute should trigger native form submission when clicked, regardless of whether an explicitonClickhandler is provided.Reproducible example
Suggested solution
The issue appears to be in
packages/react/slot/src/slot.tsxin themergePropsfunction. Event handlers are only composed when both parent and child have them:When only the slot's handler exists, native browser behavior is somehow prevented. When handlers are composed, native behavior is preserved.
Note: A wrapper component workaround does not work:
Because Slot uses children.props (the immediate child element's props), not the rendered output's props, the internal onClick never participates in the merge.
The fix may involve ensuring the slot-only handler path doesn't interfere with native form submission, or always wrapping handlers in a way that preserves native behavior.
Additional context
This affects any use case where a native HTML form button is used inside a Radix component with
asChild, particularly when the form element is outside the dropdown (to avoid DOM disconnection when the dropdown closes).Your environment