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
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,
}));
