I’ve been struggling with styled components and transient props. See code below.
- I have a styled component, let’s call it
StyledLabel
, that receives a transient prop. - The
StyledLabel
is used in a function component, let’s call itIntermediateLabel
. - The
IntermediateLabel
passes the props it receives to theStyledComponent
. - The
IntermediateLabel
is restyled in a styled component, let’s call itExtraStyledLabel
Now to the problem, when passing the transient prop to the ExtraStyledLabel
it is not passed to the IntermediateLabel
. But when using the IntermediateLabel
directly, it is passed to the StyledLabel
.
Is it not possible to restyle function components? Am I doing something wrong or is it a bug?
Here is the code, and Codepen https://codepen.io/tengl/pen/abxgzdL
const { createRoot } = ReactDOM;
const { useState, forwardRef } = React;
const { createGlobalStyle } = styled;
const GlobalStyle = createGlobalStyle`
html, body, #app {
height: 100%;
min-height: 100%;
}
`;
const App = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;
const StyledLabel = styled.p`
color: ${(p) => (p.$isSelected ? "green" : "red")};
font-family: "Roboto Mono", monospace;
font-size: 20px;
`;
// WORKS
const IntermediateLabelStyled = styled(StyledLabel)``;
// WORKS
const IntermediateLabelComponent = forwardRef(({ children, ...props }) => {
console.log(props.$isSelected);
return <StyledLabel {...props}>{children} </StyledLabel>;
});
// WORKS
const IntermediateLabelForwardRef = ({ children, ...props }) => {
console.log(props.$isSelected);
return <StyledLabel {...props}>{children} </StyledLabel>;
};
// WORKS
const ExtraStyledLabelStyledComponents = styled(IntermediateLabelStyled)`
font-weight: bold;
font-size: 30px;
`;
// DOES NOT WORK
const ExtraStyledLabelComponent = styled(IntermediateLabelComponent)`
font-weight: bold;
font-size: 30px;
`;
// DOES NOT WORK
const ExtraStyledLabelForwardRef = styled(IntermediateLabelForwardRef)`
font-weight: bold;
font-size: 30px;
`;
const Container = () => (
<App>
<GlobalStyle />
<StyledLabel $isSelected>React 18 + styled-components 6. Transient props in intermediate components</StyledLabel>
<StyledLabel $isSelected>Green text is ok, red is not</StyledLabel>
<ExtraStyledLabelStyledComponents $isSelected={true}>With styled component</ExtraStyledLabelStyledComponents>
<ExtraStyledLabelComponent $isSelected={true}>With React component</ExtraStyledLabelComponent>
<ExtraStyledLabelForwardRef $isSelected={true}>With forwardRef</ExtraStyledLabelForwardRef>
<IntermediateLabelComponent $isSelected={true}>Function component directly</IntermediateLabelComponent>
</App>
);
const root = createRoot(document.getElementById("app"));
root.render(<Container />);
2
Answers
It seems that it is by design. https://github.com/styled-components/styled-components/issues/4266
I tried with
shouldForwardProp
but couldn't make it work anyway.The solution will be to rename it to a normal prop.
It is not possible since it was designed that way. Stated in the documentation (https://styled-components.com/docs/api#transient-props) that:
This means that it’s behaving correctly for the React node to not be able to access the transient props. You can use normal props and filtering them manually instead.