Skip to content

Instantly share code, notes, and snippets.

@richiethomas
Created August 12, 2025 20:42
Show Gist options
  • Save richiethomas/5b727be9cf6f2a6142c3a311b64c60be to your computer and use it in GitHub Desktop.
Save richiethomas/5b727be9cf6f2a6142c3a311b64c60be to your computer and use it in GitHub Desktop.
Skip to content
Chat history
You said:
How to pass in a component's property value to dynamically determine the styling of a React styled component?
ChatGPT said:
Thought for 9s
Use prop-based interpolations in your styled component. Pass the value as a prop and read it inside the template literal. With styled-components, prefer transient props (prefix with $) so React doesn’t forward unknown attributes to the DOM.
Basic (styled-components, JS)
import styled, { css } from "styled-components";
const Button = styled.button`
padding: ${({ $size }) =>
$size === "lg" ? "0.85rem 1.25rem" :
$size === "sm" ? "0.4rem 0.7rem" :
"0.6rem 1rem"};
background: ${({ $intent, theme }) =>
$intent === "danger" ? theme.colors.red :
$intent === "success" ? theme.colors.green :
theme.colors.gray};
${({ $active }) =>
$active &&
css`
box-shadow: 0 0 0 3px rgba(0,0,0,.12);
transform: translateY(-1px);
`}
`;
export default function Example({ isPrimary }) {
return (
<Button
$size="lg"
$intent={isPrimary ? "success" : "danger"}
$active={isPrimary}
>
Click me
</Button>
);
}
Why the $ prefix?
$size, $intent, $active won’t be attached as DOM attributes, avoiding warnings like React does not recognize the 'intent' prop on a DOM element. The values remain available to the style function.
TypeScript version
type Size = "sm" | "md" | "lg";
type Intent = "default" | "success" | "danger";
interface ButtonProps {
$size?: Size;
$intent?: Intent;
$active?: boolean;
}
const Button = styled.button<ButtonProps>`
/* same CSS as above */
`;
Alternative: filter props without $
If you don’t want transient props, configure shouldForwardProp:
const Button = styled.button.withConfig({
shouldForwardProp: (prop) => !["variant", "active"].includes(prop)
})`
background: ${({ variant }) => (variant === "primary" ? "black" : "white")};
color: ${({ variant }) => (variant === "primary" ? "white" : "black")};
opacity: ${({ active }) => (active ? 1 : 0.6)};
`;
Dynamic values beyond conditionals
You can compute any CSS value from props:
const Tag = styled.span`
font-size: ${({ $scale = 1 }) => `${0.875 * $scale}rem`};
border: 1px solid ${({ $color = "#999" }) => $color};
color: ${({ $color = "#999" }) => $color};
`;
Using the theme + props together
const Card = styled.div`
border-radius: ${({ theme }) => theme.radii.lg};
border: 1px solid
${({ theme, $elevated }) => ($elevated ? theme.colors.primary : theme.colors.border)};
`;
Emotion (same idea)
/** @jsxImportSource @emotion/react */
import styled from "@emotion/styled";
const Box = styled("div", {
shouldForwardProp: (prop) => prop !== "state"
})(({ state }) => ({
padding: "1rem",
background: state === "ok" ? "#e6ffed" : "#fff5f5",
}));
Common pitfalls
Don’t pass raw, non-standard props to DOM nodes (use $ or shouldForwardProp).
Keep the style logic close to the component—avoid scattered conditionals in parents; just pass simple props.
For many combinations, consider a variant map to keep styles tidy.
Variant map pattern (scales well)
const intents = {
default: { bg: "#eee", fg: "#111" },
primary: { bg: "#0d6efd", fg: "#fff" },
danger: { bg: "#dc3545", fg: "#fff" },
} as const;
type IntentKey = keyof typeof intents;
const Button = styled.button<{ $intent?: IntentKey }>`
${({ $intent = "default" }) => {
const v = intents[$intent];
return css`
background: ${v.bg};
color: ${v.fg};
`;
}}
`;
That’s all you need: pass a prop, read it in the styled function, and (ideally) use transient props to keep the DOM clean.
No file chosenNo file chosen
ChatGPT can make mistakes. Check important info.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment