Keep styling props out of the DOM with styled-components transient props
Use styled-components transient props prefixed with $ to keep styling-only props like variant and size out of the DOM, removing React unknown prop warnings while preserving CSS interpolation.
31 Dec 2025, 17:50 UTC

Passing variant or size to a styled button feels natural for a design system, until React warns about an unknown prop on a DOM element and you see variant="primary" in the markup. The useful takeaway is to keep styling-only props out of the DOM by using styled-components transient props, the $ prefix convention that is stripped before rendering.
The leak problem in styled-components
Styled-components forwards component props to the underlying DOM element by default. That is intentional for props like onClick or type, but it is a problem for props that only exist to drive CSS interpolation.
When a component is built as styled.button, any prop you pass that is not a valid HTML attribute ends up on the real <button>. React will warn about unknown props in development and the attribute will appear in the DOM in production, which is noisy for consumers and can break strict HTML validators.
This matters most in design system primitives that rely heavily on variant and state props, and that are composed with ThemeProvider and design tokens.
Transient props and the $ prefix
Starting in styled-components v5, props prefixed with $ are treated as transient. The library strips them before the props reach the DOM, but they remain available inside the template literal for CSS interpolation.
The convention is team-level, not a language feature. $variant is readable inside the style definition and clearly signals "styling only". It is not forwarded, so no React unknown prop warning is produced and the markup stays clean.
Transient props work well with theming. You can read $variant to pick a color from theme.colors without leaking the implementation detail to the consumer.
Worked example: a Button with $variant and $size
The following illustrates the pattern. It assumes styled-components v5 or later.
import styled from 'styled-components';
type ButtonProps = {
$variant?: 'primary' | 'secondary';
$size?: 'sm' | 'md';
};
const Button = styled.button<ButtonProps>`
padding: ${({ $size }) => $size === 'sm' ? '6px 12px' : '10px 16px'};
border-radius: 6px;
border: none;
font-weight: 600;
background: ${({ $variant, theme }) =>
$variant === 'secondary' ? theme.colors.gray : theme.colors.primary};
color: white;
cursor: pointer;
`;
// Usage
<Button $variant="primary" $size="md">Save</Button>
The component renders a plain <button> with no variant or size attributes. The $ props drive the CSS only.
To verify behavior, render the component in a development app and inspect the DOM in browser dev tools. The attribute should be absent. If you temporarily rename $variant to variant, React will emit an unknown prop warning in development, confirming the stripping effect.
Trade-offs and limitations
Transient prop stripping is version sensitive. It is stable in v5 and later; earlier versions handled prop forwarding differently.
TypeScript requires explicit typing for transient props. If the prop types are not declared on the styled component, type checking can be misleading about which props reach the DOM.
The $ naming convention can confuse new team members. Developers may try to read $variant in children or assume it is a regular prop. Document the convention in your component API and keep transient props internal to the styled definition when possible.
Do not use transient props for values that consumers need to read or control at runtime. They are for styling only.
Actionable closing
Adopt $ prefixed props for any styling-only input on styled DOM elements. Audit existing primitives for leaked props like variant, size, active, and migrate them to transient form. Add a short note to your design system docs explaining the convention and the version requirement.
Check the result by inspecting the rendered markup and confirming no styling props appear in the DOM while the visual output and generated CSS remain unchanged.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.