Reputation: 1651
I'm using styled-components to override styling from an existing component, in this case ToggleButton
from material ui. However I want my new component to have one additional property (hasMargin
) that controls the style:
import {ToggleButton} from "@material-ui/lab";
import styled, {css} from "styled-components";
const StyledToggleButton = styled(ToggleButton)<{ hasMargin?: boolean }>`
&& {
color: red;
${props => props.hasMargin &&
css`
margin: 10px;
`}
}
`;
My intention is that StyledToggleButton
behaves exactly like ToggleButton
but has red color and can be called with an additional property hasMargin
to turn on the margin css. I want to call it like this:
<StyledToggleButton
value={""} // ToggleButton prop
size={"small"} // ToggleButton prop
hasMargin={true} // My prop from StyledToggleButton
>
click
</StyledToggleButton>
But if I do it like this, in the browser console I get:
Warning: React does not recognize the `hasMargin` prop on a DOM element.
This is because StyledToggleButton
seems to also pass hasMargin
further to ToggleButton
and ToggleButton
of course can't and shouldn't deal with that (passing it down to the DOM element where it makes no sense). What is the best way to rewrite my code, so hasMargin
is only processed by StyledToggleButton
?
I've made a codesandbox to illustrate the issue. Thank you!
Upvotes: 16
Views: 18083
Reputation: 9248
This is exactly what transient props are for.
All you need to do is prefix the prop with a $
and styled-components
won't pass the prop to the underlying DOM element.
// Note the $ prefix in the prop name 👇
const StyledToggleButton = styled(ToggleButton)<{ $hasMargin?: boolean }>`
&& {
color: red;
// Just use the prop as normal
${(props) =>
props.$hasMargin &&
css`
margin: 10px;
`}
}
`;
<StyledToggleButton
value={""}
size={"small"}
{/* Assign the prop as normal */}
$hasMargin={true}
>
click
</StyledToggleButton>;
Here's your updated Codesandbox link with that error gone.
Upvotes: 28
Reputation: 867
What about wrapping the ToggleButton with span and applying styles from there?
const StyledToggleButton = styled.span<{ hasMargin?: boolean }>`
margin: ${props => props.hasMargin && '50px'};
button {
color: red !important;
}
`
interface MyButtonProps extends ToggleButtonProps {
hasMargin?: boolean;
}
function MyButton(props: MyButtonProps) {
const { hasMargin, ...toggleButtonProps } = props;
return (
<StyledToggleButton hasMargin={hasMargin} >
<ToggleButton {...toggleButtonProps}>
{props.children}
</ToggleButton>
</StyledToggleButton>
);
}
export default function App() {
return (
<div className="App">
<MyButton value={""}>click</MyButton>
<MyButton value={""} size={"small"} hasMargin={true}>
click
</MyButton>
</div>
);
}
Upvotes: 0