If you're using a built-in components like div or span and you want to allow the user to customize the styles via some props.
const MyComponent = styled('div')(({ bgColor }) => ({
backgroundColor: bgColor,
}));
When you're using it like this:
<MyComponent bgColor='red'>
The prop is passed to the real element in the DOM tree as attribute:

And react will complain, something like:
Warning: React does not recognize the `bgColor` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `bgcolor` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
This is why shouldForwardProp exists, to prevent styling props from being passed down and create invalid attribute:
const MyComponent = styled('div', {
shouldForwardProp: (props) => props !== 'bgColor',
})(({ bgColor }) => ({
backgroundColor: bgColor,
}));
Answer from NearHuscarl on Stack OverflowIf you're using a built-in components like div or span and you want to allow the user to customize the styles via some props.
const MyComponent = styled('div')(({ bgColor }) => ({
backgroundColor: bgColor,
}));
When you're using it like this:
<MyComponent bgColor='red'>
The prop is passed to the real element in the DOM tree as attribute:

And react will complain, something like:
Warning: React does not recognize the `bgColor` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `bgcolor` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
This is why shouldForwardProp exists, to prevent styling props from being passed down and create invalid attribute:
const MyComponent = styled('div', {
shouldForwardProp: (props) => props !== 'bgColor',
})(({ bgColor }) => ({
backgroundColor: bgColor,
}));
If you are using TypeScript, I use utility function shouldForwardProp for it, so I always type prop names correctly:
export const shouldForwardProp =
<TCustomProps extends Record<string, unknown>>(customProps: ReadonlyArray<keyof TCustomProps>) =>
(prop: string): boolean =>
!customProps.includes(prop);
const MyComponent = styled('div', {
shouldForwardProp: (prop) => shouldForwardProp<MyComponentProps>(['isDisabled', 'bgColor'], prop),
})<MyComponentProps>(({ theme, isDisabled, size, bgColor }) => ({
...
Source
Yes!
The most basic example of the above would look like this in MUI v5:
const Div = styled("div")(({ primary }) => ({
backgroundColor: primary ? "palevioletred" : "white",
color: primary ? "white" : "palevioletred"
}));
render(
<section>
<Div>Normal</Div>
<Div primary>Primary!</Div>
<section>
);
However, as the React docs say:
The unknown-prop warning will fire if you attempt to render a DOM element with a prop that is not recognized by React as a legal DOM attribute/property. You should ensure that your DOM elements do not have spurious props floating around.
So MUI gave us the shouldForwardProp option to tell MUI whether it "should forward the prop" to the root node or not. The above example would look like this using that prop:
const Div = styled("div", {
shouldForwardProp: (prop) => prop !== "primary"
})(({ primary }) => ({
backgroundColor: primary ? "palevioletred" : "white",
color: primary ? "white" : "palevioletred"
}));
render(
<section>
<Div>Normal</Div>
<Div primary>Primary!</Div>
<section>
);
Explanation
The second argument to the styled function is an options object, one of the things it accepts is shouldForwardProp, which as the docs say, "Indicates whether the prop should be forwarded to the Component". So to remove the unknown prop warning from the console, we tell it not to pass our custom prop to the DOM element with shouldForwardProp: (prop) => prop !== "primary". Now we destructure this prop in the function call that returns our custom styles, and use it in those styles like we would any other function.
If you want to use the global theme styles here as well, just destructure it along with your custom prop(s), ie ({ primary, otherProp, thirdProp, theme }).
Working codesandbox.
MUI v5 styled API docs
Here is a fully-working MUI v5 TypeScript example where you can pass custom properties to a styled component:
import React from 'react';
import { Button, styled, Typography } from '@mui/material';
const PREFIX = 'NimbusButton';
const classes = {
root: `${PREFIX}-root`,
button: `${PREFIX}-button`
};
interface RootProps {
textColor?: 'primary' | 'secondary';
buttonTextColor?: 'primary' | 'secondary';
}
const Root = styled('div', {
shouldForwardProp: (prop) => prop !== 'textColor' && prop !== 'buttonTextColor',
name: 'MyThemeComponent',
slot: 'Root'
})<RootProps>(({ theme, textColor, buttonTextColor }) => ({
[`& .${classes.root}`]: {
color: textColor ? theme.palette.primary.main : theme.palette.secondary.main
},
[`& .${classes.button}`]: {
color: buttonTextColor ? theme.palette.primary.main : theme.palette.secondary.main
}
}));
type OwnProps = {
textColor: 'primary' | 'secondary';
buttonTextColor: 'primary' | 'secondary';
text?: string;
buttonText: string;
};
const CustomStyledButton: React.FC<OwnProps> = (props) => {
const { textColor, buttonTextColor, text, buttonText } = props;
return (
<Root className={classes.root} textColor={textColor} buttonTextColor={buttonTextColor}>
{text && <Typography variant={'body1'}>{text}</Typography>}
<Button className={classes.button}>{buttonText}</Button>
</Root>
);
};
export default CustomStyledButton;
In all the APIs that you've used here, you've used them incorrectly. I've annotated the mistakes you've made in this TS Playground link. But I'll add it here as well -
// original
const Thing = styled.div<{
isSmallContainer: boolean;
}>(({ isSmallContainer }) => ({
// padding: isSmallContainer ? 12 : '16px 32px',
// The type of padding in the library is `string & {} | 0` so you can either set it to zero or a string
padding: isSmallContainer ? "12px" : '16px 32px',
display: 'flex',
alignItems: 'center',
}));
const Thing2 = styled(
'div',
// The function takes only one argument so can't take BASE_CONFIG
// BASE_CONFIG
)<{
isSmallContainer: boolean;
}>(({ isSmallContainer }) => ({
// padding: isSmallContainer ? 12 : '16px 32px',
// The type of padding in the library is `string & {} | 0` so you can either set it to zero or a string
padding: isSmallContainer ? "12px" : '16px 32px',
display: 'flex',
alignItems: 'center',
}));
const Thing3 = styled
.div<{
isSmallContainer: boolean;
}>(({ isSmallContainer }) => ({
// padding: isSmallContainer ? 12 : '16px 32px',
// The type of padding in the library is `string & {} | 0` so you can either set it to zero or a string
padding: isSmallContainer ? "12px" : '16px 32px',
display: 'flex',
alignItems: 'center',
}))
// Not Supported by API
// .withConfig(BASE_CONFIG);
const Thing4 = styled
.div<{
isSmallContainer: boolean;
}>
// .withConfig(BASE_CONFIG) Cannot call a generic
(({ isSmallContainer }) => ({
// padding: isSmallContainer ? 12 : '16px 32px',
// The type of padding in the library is `string & {} | 0` so you can either set it to zero or a string
padding: isSmallContainer ? "12px" : '16px 32px',
display: 'flex',
alignItems: 'center',
}));
const Thing5 = styled("div")
// .withConfig(BASE_CONFIG) // The params are incorrect
.withConfig({ shouldForwardProp: (prop) => prop === "isSmallContainer" ? false : true })
// Already called styled with `div` argument, no need to call again
// .div<{
// isSmallContainer: boolean;
// }>
<{isSmallContainer: boolean}>(({ isSmallContainer }) => ({
// padding: isSmallContainer ? 12 : '16px 32px',
// The type of padding in the library is `string & {} | 0` so you can either set it to zero or a string
padding: isSmallContainer ? "12px" : '16px 32px',
display: 'flex',
alignItems: 'center',
}));
const BASE_IGNORED_PROPS: Array<string | number | symbol> = [
'isSmallContainer',
];
const BASE_CONFIG: StyledConfig<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
StyledComponentPropsWithRef<React.ComponentType<any>>
> = {
shouldForwardProp: (
prop: string | number | symbol,
defaultValidatorFn: (prop: string | number | symbol) => boolean
) => !BASE_IGNORED_PROPS.includes(prop) && defaultValidatorFn(prop),
};
const Thing = styled.div(({ theme }) => ({
flex: '0 1 100%',
}));
const ThingInner = styled(Flex).withConfig(BASE_CONFIG)<{
isSmallContainer: boolean;
}>(({ isSmallContainer }) => ({
width: '100%',
padding: isSmallContainer ? 12 : 32,
paddingBottom: 0,
}));
Use Transient props
TL;DR just prefix your attriibute with
$sign. example$borderColor,$black,$any,$attribute.
If you want to prevent props meant to be consumed by styled components from being passed to the underlying React node or rendered to the DOM element, you can prefix the prop name with a dollar sign ($), turning it into a transient prop.
// typescript example
const BaseButton = styled(Button)<{ $borderColor: string }>`
border-color: ${({ $borderColor }): string => $borderColor};
`;
// js
const BaseButton = styled(Button)`
border-color: ${({$borderColor}) => $borderColor}
`;
// usage
<BaseButton $borderColor="red">Button</BaseButton>
2nd method
Checkout shouldForwardProp
const Comp = styled('div').withConfig({
shouldForwardProp: (prop, defaultValidatorFn) =>
!['hidden'].includes(prop)
&& defaultValidatorFn(prop),
}).attrs({ className: 'foo' })`
color: red;
&.foo {
text-decoration: underline;
}
`;
render(
<Comp hidden draggable="true">
Drag Me!
</Comp>
);
Your existing code was already right, but react gave you two options :
1) use lower case than snake-case
2) remove the attribute from DOM (you took this approach)
From the code I can see that you need the prop borderColor, but in the custom styling, you separated the props
({borderColor,... rest}) => <Button {...rest} />
You removed the border Color prop but you try to access in styled props the next line.
Instead try to rename the prop to bordercolor if you want warning to go away or just ignore warning.
