[MUI v6] Box component warns about TypeScript type
Steps to reproduce
Based on the example provided from MUI https://github.com/mui/material-ui/tree/master/examples/material-ui-nextjs-ts
I have added the following code
import * as React from 'react';
import Container from '@mui/material/Container';
import Box, { BoxProps } from '@mui/material/Box';
function Li({ children, ...other }: BoxProps) {
return (
<Box
component={'li'}
{...other}
>
{children}
</Box>
);
}
function Ul({ children, ...other }: BoxProps) {
return (
<Box
component={'ul'}
{...other}
>
{children}
</Box>
);
}
function Svg({ children, ...other }: BoxProps) {
return (
<Box
component={'svg'}
width='24'
height='25'
viewBox='0 0 24 25'
fill='none'
xmlns='http://www.w3.org/2000/svg'
{...other}
>
<path
d='M4 22.5098L7.9997 15.5098H23.9998L19.9997 22.5098H4Z'
fill='#3777E3'
/>
<path
d='M16.0003 15.5098H24.0001L16.0003 1.50977H8L16.0003 15.5098Z'
fill='#FFCF63'
/>
<path
d='M0 15.5098L4.00024 22.5098L12 8.50977L7.99994 1.50977L0 15.5098Z'
fill='#11A861'
/>
</Box>
);
}
export default function Home() {
return (
<Container maxWidth='lg'>
<Svg />
<Ul>
<Li>Item 1</Li>
<Li>Item 2</Li>
<Li>Item 3</Li>
<Li>Item 4</Li>
</Ul>
</Container>
);
}
I use this method in MUI v5 without any problems. However, the error appears in MUI v6. I read in the documentation that components were removed in MUI v6 https://github.com/mui/material-ui/releases/tag/v6.0.0-rc.0
But I still see the document is still in use: https://mui.com/material-ui/react-box/#basics
So the component in Box is actually removed?
Current behavior
Error message appears
No overload matches this call.
Overload 1 of 2, '(props: { component: "li"; } & BoxOwnProps<Theme> & Omit<Omit<DetailedHTMLProps<LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, "ref"> & { ...; }, keyof BoxOwnProps<...>>): Element | null', gave the following error.
Type '{ children: ReactNode; ref?: Ref<unknown> | undefined; sx?: SxProps<Theme> | undefined; border?: ResponsiveStyleValue<number | (string & {}) | "inset" | "hidden" | "-moz-initial" | ... 194 more ... | undefined> | ((theme: Theme) => ResponsiveStyleValue<...>); ... 378 more ...; component: "li"; }' is not assignable to type 'Omit<Omit<DetailedHTMLProps<LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, "ref"> & { ref?: ((instance: HTMLLIElement | null) => void | (() => VoidOrUndefinedOnly)) | ... 2 more ... | undefined; }, keyof BoxOwnProps<...>>'.
Types of property 'onToggle' are incompatible.
Type 'ToggleEventHandler<HTMLDivElement> | undefined' is not assignable to type 'ToggleEventHandler<HTMLLIElement> | undefined'.
Type 'ToggleEventHandler<HTMLDivElement>' is not assignable to type 'ToggleEventHandler<HTMLLIElement>'.
Type 'HTMLDivElement' is missing the following properties from type 'HTMLLIElement': type, value
Overload 2 of 2, '(props: DefaultComponentProps<BoxTypeMap<{}, "div", Theme>>): Element | null', gave the following error.
Type '{ children: ReactNode; ref?: Ref<unknown> | undefined; sx?: SxProps<Theme> | undefined; border?: ResponsiveStyleValue<number | (string & {}) | "inset" | "hidden" | "-moz-initial" | ... 194 more ... | undefined> | ((theme: Theme) => ResponsiveStyleValue<...>); ... 378 more ...; component: string; }' is not assignable to type 'IntrinsicAttributes & BoxOwnProps<Theme> & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & { ...; }, keyof BoxOwnProps<...>>'.
Property 'component' does not exist on type 'IntrinsicAttributes & BoxOwnProps<Theme> & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & { ...; }, keyof BoxOwnProps<...>>'.
No overload matches this call.
Overload 1 of 2, '(props: { component: "ul"; } & BoxOwnProps<Theme> & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLUListElement>, HTMLUListElement>, "ref"> & { ...; }, keyof BoxOwnProps<...>>): Element | null', gave the following error.
Type '{ children: ReactNode; ref?: Ref<unknown> | undefined; sx?: SxProps<Theme> | undefined; border?: ResponsiveStyleValue<number | (string & {}) | "inset" | "hidden" | "-moz-initial" | ... 194 more ... | undefined> | ((theme: Theme) => ResponsiveStyleValue<...>); ... 378 more ...; component: "ul"; }' is not assignable to type 'Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLUListElement>, HTMLUListElement>, "ref"> & { ...; }, keyof BoxOwnProps<...>>'.
Types of property 'onToggle' are incompatible.
Type 'ToggleEventHandler<HTMLDivElement> | undefined' is not assignable to type 'ToggleEventHandler<HTMLUListElement> | undefined'.
Type 'ToggleEventHandler<HTMLDivElement>' is not assignable to type 'ToggleEventHandler<HTMLUListElement>'.
Type 'HTMLDivElement' is missing the following properties from type 'HTMLUListElement': compact, type
Overload 2 of 2, '(props: DefaultComponentProps<BoxTypeMap<{}, "div", Theme>>): Element | null', gave the following error.
Type '{ children: ReactNode; ref?: Ref<unknown> | undefined; sx?: SxProps<Theme> | undefined; border?: ResponsiveStyleValue<number | (string & {}) | "inset" | "hidden" | "-moz-initial" | ... 194 more ... | undefined> | ((theme: Theme) => ResponsiveStyleValue<...>); ... 378 more ...; component: string; }' is not assignable to type 'IntrinsicAttributes & BoxOwnProps<Theme> & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & { ...; }, keyof BoxOwnProps<...>>'.
Property 'component' does not exist on type 'IntrinsicAttributes & BoxOwnProps<Theme> & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & { ...; }, keyof BoxOwnProps<...>>'.
No overload matches this call.
Overload 1 of 2, '(props: { component: "svg"; } & BoxOwnProps<Theme> & Omit<Omit<SVGProps<SVGSVGElement>, "ref"> & { ref?: ((instance: SVGSVGElement | null) => void | (() => VoidOrUndefinedOnly)) | RefObject<...> | null | undefined; }, keyof BoxOwnProps<...>>): Element | null', gave the following error.
Type '{ children: Element[]; ref?: Ref<unknown> | undefined; sx?: SxProps<Theme> | undefined; border?: ResponsiveStyleValue<number | (string & {}) | "inset" | "hidden" | "-moz-initial" | ... 194 more ... | undefined> | ((theme: Theme) => ResponsiveStyleValue<...>); ... 381 more ...; xmlns: string; }' is not assignable to type 'Omit<Omit<SVGProps<SVGSVGElement>, "ref"> & { ref?: ((instance: SVGSVGElement | null) => void | (() => VoidOrUndefinedOnly)) | RefObject<...> | null | undefined; }, keyof BoxOwnProps<...>>'.
Types of property 'onCopy' are incompatible.
Type 'ClipboardEventHandler<HTMLDivElement> | undefined' is not assignable to type 'ClipboardEventHandler<SVGSVGElement> | undefined'.
Type 'ClipboardEventHandler<HTMLDivElement>' is not assignable to type 'ClipboardEventHandler<SVGSVGElement>'.
Type 'HTMLDivElement' is missing the following properties from type 'SVGSVGElement': currentScale, currentTranslate, height, width, and 53 more.
Overload 2 of 2, '(props: DefaultComponentProps<BoxTypeMap<{}, "div", Theme>>): Element | null', gave the following error.
Type '{ children: Element[]; ref?: Ref<unknown> | undefined; sx?: SxProps<Theme> | undefined; border?: ResponsiveStyleValue<number | (string & {}) | "inset" | "hidden" | "-moz-initial" | ... 194 more ... | undefined> | ((theme: Theme) => ResponsiveStyleValue<...>); ... 381 more ...; xmlns: string; }' is not assignable to type 'IntrinsicAttributes & BoxOwnProps<Theme> & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & { ...; }, keyof BoxOwnProps<...>>'.
Property 'component' does not exist on type 'IntrinsicAttributes & BoxOwnProps<Theme> & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & { ...; }, keyof BoxOwnProps<...>>'.
Expected behavior
No TypeScript errors appeared.
I would like to understand clearly how to use Box in MUI v6.
Because in the project I use a lot of places like:
- <Box component={'li'}
- <Box component={'ul'}
- <Box component={'svg'} ...
So it will take a lot of time to restructure the whole thing.
Looking forward to suitable suggestions for this situation. Thanks!
I have the same issue. Waiting for maintainer. Thanks
I have the same issue. Waiting for maintainer. Thanks
Same here!
The trick here is to use a type generic:
function Li({ children, ...other }: BoxProps<'li'>) {
function Ul({ children, ...other }: BoxProps<'ul'>) {
function Svg({ children, ...other }: BoxProps<'svg'>) {
See demo: typescript playground
This ensures that TypeScript understands what's going on, and also makes sure that you can, for example, pass the viewBox prop to the Svg component but not to the Li and Ul components.
The trick here is to use a type generic:
function Li({ children, ...other }: BoxProps<'li'>) { function Ul({ children, ...other }: BoxProps<'ul'>) { function Svg({ children, ...other }: BoxProps<'svg'>) {See demo: typescript playground
This ensures that TypeScript understands what's going on, and also makes sure that you can, for example, pass the
viewBoxprop to theSvgcomponent but not to theLiandUlcomponents.
This works perfectly for me, thank you!
The trick here is to use a type generic:
function Li({ children, ...other }: BoxProps<'li'>) { function Ul({ children, ...other }: BoxProps<'ul'>) { function Svg({ children, ...other }: BoxProps<'svg'>) {See demo: typescript playground
This ensures that TypeScript understands what's going on, and also makes sure that you can, for example, pass the
viewBoxprop to theSvgcomponent but not to theLiandUlcomponents.
@bartlangelaan Thank you it works well. But is there any solution when we use with styled() ?
<WithStyled3 active component="nav" sx={{ color: 'white' }} />
const WithStyled3 = styled(Box, {
shouldForwardProp: (prop) => !['active'].includes(prop as string),
})<{ active?: boolean }>(({ theme }) => ({
width: 100,
height: 50,
marginBottom: 24,
backgroundColor: theme.palette.info.main,
variants: [
{
props: { active: true },
style: {
backgroundColor: theme.palette.success.main,
},
},
],
})) as typeof Box;
https://stackblitz.com/edit/github-p7amox?file=src%2Fapp%2Fpage.tsx,package.json
In that case, you can just style a div instead of a Box. The styled utility always returns a component that has the sx prop available - you don't need a Box for that.
Instead of passing a component prop, you can pass the as prop.
// Instead of the 'component' prop, use the 'as' prop.
<WithStyled3 active as="nav" sx={{ color: 'white' }} />
// Style a 'div' instead of 'Box'.
const WithStyled3 = styled('div', {
shouldForwardProp: (prop) => !['active'].includes(prop as string),
})<{ active?: boolean }>(({ theme }) => ({
// styling.
// You don't need the 'as typeof Box' anymore.
}));
@bartlangelaan Thank you very much as= solved in this case.
Can you provide documentation with as= ?
Previously I only used component= without knowing about as= . I also can't find the official documentation page for this props (as=) on mui.com
The reason I want to use Box is because I want to inherit the default properties from Box to make the code more concise
In MUI v5 I just do this
<NavItemv5 active sx={{ color: 'white' }} />
const NavItemv5 = React.forwardRef<
HTMLUListElement,
BoxProps & { active?: boolean }
>(({ active, sx, ...other }, ref) => {
return (
<WithMuiv5 ref={ref} component="nav" active={active} sx={sx} {...other}>
<span>Icon</span>
<span>Text</span>
<span>Arrow</span>
</WithMuiv5>
);
});
const WithMuiv5 = styled(Box, {
shouldForwardProp: (prop) => !['active'].includes(prop as string),
})<{ active?: boolean }>(({ theme }) => ({
width: 100,
height: 50,
marginBottom: 24,
backgroundColor: theme.palette.info.main,
variants: [
{
props: { active: true },
style: {
backgroundColor: theme.palette.success.main,
},
},
],
}));
But now v6 it becomes like this
<NavItemv6 active sx={{ color: 'white' }} />
const NavItemv6 = React.forwardRef<
HTMLUListElement,
React.HTMLAttributes<HTMLUListElement> & {
active?: boolean;
sx?: SxProps<Theme>;
}
>(({ active, sx, ...other }, ref) => {
return (
<WithMuiv6 ref={ref} active={active} sx={sx} {...other}>
<span>Icon</span>
<span>Text</span>
<span>Arrow</span>
</WithMuiv6>
);
});
const WithMuiv6 = styled('ul', {
shouldForwardProp: (prop) => !['active', 'sx'].includes(prop as string),
})<{ active?: boolean }>(({ theme }) => ({
width: 100,
height: 50,
marginBottom: 24,
backgroundColor: theme.palette.info.main,
variants: [
{
props: { active: true },
style: {
backgroundColor: theme.palette.success.main,
},
},
],
}));
It is much more cumbersome to define types
React.HTMLAttributes<HTMLUListElement> & { sx?: SxProps<Theme>}React.HTMLAttributes<HTMLSpanElement> & { sx?: SxProps<Theme>}React.LiHTMLAttributes<HTMLLIElement> & { sx?: SxProps<Theme>}...
instead of BoxProps
https://stackblitz.com/edit/github-p7amox?file=src%2Fapp%2Fpage.tsx,src%2Fapp%2Flayout.tsx
If you use React.ComponentProps, it automatically takes the sx prop into account:
const NavItemv6 = React.forwardRef<
HTMLUListElement,
React.ComponentProps<typeof WithMuiv6>
>(({ active, sx, ...other }, ref) => {
return (
<WithMuiv6 ref={ref} active={active} sx={sx} {...other}>
<span>Icon</span>
<span>Text</span>
<span>Arrow</span>,<li></li>
</WithMuiv6>
);
});
And because the active and sx prop are passed as-is, you can keep them in the other props if you want:
const NavItemv6 = React.forwardRef<
HTMLUListElement,
React.ComponentProps<typeof WithMuiv6>
>(({ ...other }, ref) => {
return (
<WithMuiv6 ref={ref} {...other}>
<span>Icon</span>
<span>Text</span>
<span>Arrow</span>,<li></li>
</WithMuiv6>
);
});
@bartlangelaan
Thanks for your efforts to help so far but in my case React.ComponentProps<typeof WithMuiv6> is shared in many places and this doesn't seem possible to me.
Maybe I will use this method. Although it is more cumbersome than the old method (MUI v5), it is more flexible
// file.types.ts
export type NavItemProps = BoxProps<'ul'> & {
open?: boolean;
active?: boolean;
};
...
// file.item.ts
const NavItemv6 = forwardRef<HTMLUListElement, NavItemProps>(
({ active, children, open, ...other }, ref) => (
<WithMuiv6 ref={ref} open={open} active={active} {...other}>
{children}
<span> Icon </span>
<span> Text </span>
</WithMuiv6>
)
);
const WithMuiv6 = styled(
forwardRef((props: NavItemProps, ref) => <Box {...props} ref={ref} component="ul" />),
{
shouldForwardProp: (prop) => !['open', 'active'].includes(prop as string),
}
)(({ theme }) => ({
width: 100,
height: 50,
display: 'flex',
marginBottom: 24,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: theme.palette.secondary.main,
variants: [
{
props: { active: true },
style: {
backgroundColor: theme.palette.success.main,
},
},
{
props: { open: true },
style: {
backgroundColor: theme.palette.common.black,
},
},
],
}));
Building on earlier comments here, I settled on this:
// Before
function MyCustomComponent({ component, ...props }: BoxProps) {
return <Box component={component} {...props} />
}
// After
const Bachs = styled('div')()
function MyCustomComponent({ as, ...props }: ComponentProps<typeof Bachs>) {
return <Bachs as={as} {...props} />
}
This means I have to refactor my other code to send as instead of component, but otherwise it seems to work fine.
Just stumbled upon this section of the docs. Based on that, I ended up with this:
import { ElementType } from 'react'
const MyComponent = (boxProps: BoxProps<ElementType, { component?: ElementType }>) => {
return <Box {...boxProps} />
}
Or with ref forwarding:
import { ElementType, ForwardedRef } from 'react'
const MyComponent = (
boxProps: BoxProps<ElementType, { component?: ElementType }>,
ref: ForwardedRef<ElementType | null>
) => {
return <Box {...boxProps} ref={ref} />
}
const MyComponentWithRef = forwardRef<ElementType | null, BoxProps<ElementType>>(MyComponent)
This avoids involved styled() and it means I can keep using the component prop instead of switching to as.
Edit: just noticed my ref solution accepts garbage, e.g. <MyComponentWithRef component="foo">. The first solution works, though.
Hey everyone! Thanks for the reports and the patience.
This is an oversight of https://github.com/mui/material-ui/pull/43384, as the Material UI and System BoxProps types should both have had the component, but Material UI's didn't.
I opened a PR to fix this: https://github.com/mui/material-ui/pull/44643 Sorry for the inconvenience.
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.
[!NOTE] @mtr1990 How did we do? Your experience with our support team matters to us. If you have a moment, please share your thoughts in this short Support Satisfaction survey.
The fix has been merged, and it will be available starting from the next release (>6.1.10)