Select and Menu components freeze inside custom HTMLElement
Steps to reproduce
minimal code example:
import React, { createRef } from 'react';
import { createRoot } from 'react-dom/client';
import Select from '@mui/material/Select';
import MenuItem from '@mui/material/MenuItem';
class MyCustomElement extends HTMLElement {
connectedCallback() {
this._root = createRoot(this);
this._root.render(<MySelect />);
}
disconnectedCallback() {
this._root.unmount();
}
}
const MySelect = () => {
const [value, setValue] = React.useState('');
const ref = createRef();
return (
<Select
value={value}
onChange={(e) => setValue(e.target.value)}
inputRef={ref}
>
<MenuItem value="one">One</MenuItem>
<MenuItem value="two">Two</MenuItem>
</Select>
);
};
customElements.define('my-custom-element', MyCustomElement);
Current behavior
When using MUI Select inside a custom HTMLElement with React createRoot(this) opening the Select causes the page to freeze and throws:
useTimeout.js:25 Uncaught TypeError: fn is not a function
This appears to be related to MUI Select relying on Popover + Grow + internal refs, which break when the component is rendered directly inside a custom element.
using transitionDuration={0} into MenuProps fixes the issue, but it's not ideal
Expected behavior
The Select dropdown should open without freezing, similar to how Popover works in a stable container.
Context
No response
Your environment
npx @mui/envinfo
System: OS: macOS 14.6.1 Binaries: Node: 20.8.0 - /Users/patriciaromaniuc/.nvm/versions/node/v20.8.0/bin/node npm: 10.1.0 - /Users/patriciaromaniuc/.nvm/versions/node/v20.8.0/bin/npm pnpm: 8.9.0 - /Users/patriciaromaniuc/.nvm/versions/node/v20.8.0/bin/pnpm Browsers: Chrome: 142.0.7444.60 Edge: Not Found Firefox: 137.0 Safari: 18.3.1 npmPackages: @emotion/react: 11.14.0 @emotion/styled: 11.14.1 @mui/core-downloads-tracker: 7.3.4 @mui/icons-material: 7.3.4 @mui/material: 7.3.4 @mui/types: 7.4.7 @types/react: 19.2.2 react: 18.2.0 => 18.2.0 react-dom: 18.2.0 => 18.2.0 styled-components: 5.3.11 typescript: 3.9.10
Search keywords: useTimeout, Select, Menu, Grow
Please provide a live reproduction in the form of Stackblitz or CodeSandbox. Here's a good starting template: https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-vite-ts?file=src%2FApp.tsx
@ZeeshanTamboli can I work on this issue ?
I tried to make an example here https://stackblitz.com/edit/github-gshwyya8?file=src%2FApp.jsx but something is off with the custom elements declaration in this type of environment. @rithik56 if you want to help, that would be great.
I did add a workaround to this issue and but it's not great since the focus is also an issue and I am basically trying to make it look like MUI transition without it
import React from 'react';
import { Select } from '@mui/material';
export const WebComponentSafeTransition = React.forwardRef(({ children, in: inProp }, ref) => {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
if (inProp) {
// Use RAF to ensure the element is mounted before applying the visible class
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setMounted(true);
});
});
} else {
setMounted(false);
}
}, [inProp]);
if (!inProp && !mounted) {
return null;
}
const style = {
opacity: mounted ? 1 : 0,
transform: mounted ? 'scaleY(1) translateY(0)' : 'scaleY(0.3) translateY(-10px)',
transition: 'opacity 350ms cubic-bezier(0.4, 0, 0.2, 1), transform 350ms cubic-bezier(0.2, 1, 0.2, 1)',
transformOrigin: 'top center',
};
return (
<div ref={ref} style={style}>
{children}
</div>
);
});
/**
* Web Component-safe Select wrapper
*
* Fixes MUI Select crash when used inside custom HTMLElements with createRoot(this)
*
* Required fixes:
* - disablePortal: true - Keeps menu in Web Component's React tree
* - TransitionComponent: Custom - Bypasses Grow transition's useTimeout
*
* Features:
* - Dropdown-style animation (scaleY + translateY)
* - Menu may be clipped by parent overflow: hidden
* - Menu won't break out of scrolling containers
*
* @param {object} props - All standard MUI Select props
*/
export const WebComponentSafeSelect = ({ MenuProps = {}, ...props }) => {
const ref = React.useRef(null);
// implement onClose and onOpen to manage focused class based on workaround for MUI bug found in stackoverflow
const onClose = () => {
ref.current?.previousSibling?.classList.remove('Mui-focused');
ref.current?.classList.remove('Mui-focused');
if (props.onClose) {
props.onClose();
}
};
const onOpen = () => {
ref.current?.previousSibling?.classList.add('Mui-focused');
ref.current?.classList.add('Mui-focused');
if (props.onOpen) {
props.onOpen();
}
};
return (
<Select
{...props}
ref={ref}
autoFocus={false}
onOpen={onOpen}
onClose={onClose}
MenuProps={{
...MenuProps,
disablePortal: true,
TransitionComponent: WebComponentSafeTransition,
disableAutoFocusItem: true,
PaperProps: {
style: {
maxHeight: '300px',
marginTop: '4px',
...MenuProps.PaperProps?.style,
},
...MenuProps.PaperProps,
},
}}
/>
);
};
export default WebComponentSafeSelect;
We can't help without a clear reproduction. You can also provide a minimal example in the form of a GitHub repository with steps to reproduce the issue.
Update: found a solution and the components now work by adding transitionDuration={{ enter: 225, exit: 195 }} .
It seems like the issue is that the defaults for the react-transition-group ar missing in this type of setup with custom HTMLElement (vs normal behavior of simple react components)
Hope this helps anyone that is facing this issue.
@PatriciaRomaniuc Great, will close this issue then.
This issue has been closed. If you have a similar problem but not exactly the same, please open a new issue. Now, if you have additional information related to this issue or things that could help future readers, feel free to leave a comment.