There have been some recent developments and with a new version of Typescript (eg. 3.0.1) and styled-components (eg. 3.4.5) there's no need for a separate helper. You can specify the interface/type of your props to styled-components directly.
interface Props {
onPress: any;
src: any;
width: string;
height: string;
}
const Icon = styled.Image<Props>`
width: ${p => p.width};
height: ${p => p.height};
`;
and if you want to be more precise and ignore the onPress
const Icon = styled.Image<Pick<Props, 'src' | 'width' | 'height'>>`
width: ${p => p.width};
height: ${p => p.height};
`;
Answer from elnygren on Stack OverflowHow do I provide props to styled elements in Typescript?
Typescript using custom props
React — Passing props with styled-components
Updated packages, now my styled-components pass all their custom props to the DOM, triggering an Unknown Prop Warning everywhere...
Videos
There have been some recent developments and with a new version of Typescript (eg. 3.0.1) and styled-components (eg. 3.4.5) there's no need for a separate helper. You can specify the interface/type of your props to styled-components directly.
interface Props {
onPress: any;
src: any;
width: string;
height: string;
}
const Icon = styled.Image<Props>`
width: ${p => p.width};
height: ${p => p.height};
`;
and if you want to be more precise and ignore the onPress
const Icon = styled.Image<Pick<Props, 'src' | 'width' | 'height'>>`
width: ${p => p.width};
height: ${p => p.height};
`;
The easiest way as styled-components docs said:
import styled from 'styled-components';
import Header from './Header';
const NewHeader = styled(Header)<{ customColor: string }>`
color: ${(props) => props.customColor};
`;
// Header will also receive props.customColor
I believe what the documentation is saying is that you should avoid including your styles inside of the rendering component:
DO THIS
const StyledWrapper = styled.div`
/* ... */
`
const Wrapper = ({ message }) => {
return <StyledWrapper>{message}</StyledWrapper>
}
INSTEAD OF THIS
const Wrapper = ({ message }) => {
// WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!!
const StyledWrapper = styled.div`
/* ... */
`
return <StyledWrapper>{message}</StyledWrapper>
}
Because what happens is when the component's Props changes, then the component will re-render and the style will regenerate. Therefore it makes sense to keep it separate.
So if you read further on to the Adapting based on props section, they explain this:
const Button = styled.button`
/* Adapt the colours based on primary prop */
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
// class X extends React.Component {
// ...
render(
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);
// }
this works because when you use the Button component in class X, it will know the props of class X without you having to tell it anything.
For your scenario, I imagine the solution would be simply:
const TabWrapper = styled.li`
display: flex;
align-items: center;
justify-content: center;
padding: 100px;
margin: 1px;
font-size: 3em;
color: ${props => (props.isSelected ? `white` : `black`)};
background-color: ${props => (props.isSelected ? `black` : `#C4C4C4`)};
cursor: ${props => (props.isSelected ? 'default' : `pointer`)};
`;
const Tab = ({ onClick, isSelected, children }) => {
return <TabWrapper onClick={onClick}>{children}</TabWrapper>
}
const X = <Tab onClick={() => console.log('clicked')} isSelected>Some Children</Tab>
I haven't tested this at all, so please feel free to try it out and let me know if it works for you or whatever worked for you!
You can pass an argument with Typescript as follows:
<StyledPaper open={open} />
...
const StyledPaper = styled(Paper)<{ open: boolean }>`
top: ${p => (p.open ? 0 : 100)}%;
`;
I was so happy in my previous repo with styled-components 5.3.6, making thousands of styled-components components with all that dynamic stuff with the help of custom props. I made sure to respect the reserved prop names like 'disabled' (I would call it isDisabled instead) and it was awesome.
Now, new project, new repo, I decide to take the latest packages, including styled-components 6.1.2 and I would adapt to new changes. For once I even managed to avoid the usual. So I work for many hours without noticing any red warning in the console related to my custom props... until today:
Warning: React does not recognize the `isPanelExpanded` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `ispanelexpanded` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
Of course it doesn't recognize it. That prop was never meant for the DOM elements. And I suppose this is why:
Dropped automatic prop filtering; use transient props ($ prefix) for stuff you don't want to be passed to child component / HTML (styled-components/releases/tag/v6.0.0)
The workarounds:
-
$isPanelExpanded. Ugly.
-
styled('div').withConfig({ shouldForwardProp: (prop) => !['isPanelExpanded'].includes(prop)}). No. Just.. no.
-
Roll back to an outdated styled-components like a piece of sh**?
Someone please tell me I missed the simple elegant solution to this nonsense. Wake me from this nightmare, please. *screams (from home, not at the office)*
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;