Single Source of Truth: Cross-Component Styling with React Compound Pattern
Single Source of Truth: Cross-Component Styling with React Compound Pattern
Cross-component styling is more than often in real world application. The blog explores a context-aware styling solutions in React applications, leveraging the Compound Pattern.
Created on Apr 23, 2025, Last Updated on Oct 19, 2025, By a Developer
When* building UI components, Its pretty common that we wanna trigger effects on component based on events on parent component, such as hovering. While applying style to components is straightforward, cross-component styling can become messy without proper control. The Blog explore an elegant solution using Compound Pattern to tackle the contextual styling challenge.
Styling is Simple
Applying a hover effect to a Button is fairly simple, A pure CSS solution is more than enough.
.Button {
background-color: transparent;
&:hover {
background-color: gray;
}
}
The Challenge: Cross-Component Hover Effect
What if we want to trigger styling transition when hovering on a parent component? Let’s say a Card. The intuitive solution is to let the parent component apply extra style to the Button.
There are multiple ways to implement this, just mention a few of them:
- Pass extra
styleorclassNameprops to theButton. - Use in the
@importsyntax to reference.Buttonstyles if using CSS modules. - Reference the component directly as
[`${Button}`]: {...}if using anyCSS-in-JSsolutions.
I will use CSS module as example for my personal preference 🙂.
@import "component/Button.module.css";
.Card {
&:hover .Button {
background-color: gray;
}
}
Problems with the Naive Approach
The solution have several drawbacks:
- Tight coupling
ButtonandCardcomponents together. - Duplicating the rule
background-color: gray;in both component. - Maintainence become challenging, as the similar rule sets would spread through the codebase as this use case increases.
As developers, we hate duplicate things. Thus, I would be happy to extract this out to have it in a util file. However, naming it as a util is inappropriate, since it only serves Button component.
Compound Pattern excels at solving these type of issue by breaking component into multiple pieces that communicate in the background to accomplish certain behavior. Unlike normal implementations leverage context API to share states, browser’s CSS cascade do the heavy lifting for us 😛.
The Compound Pattern Solution
Again, fairly simple style sheet.
.Button {
background-color: transparent;
&:hover {
background-color: gray;
}
}
.HoverContext {
&:hover .Button {
background-color: gray;
}
}
And in the React component code, a simple approach is to export the context className.
import * as React from 'react'
import buttonStyle from './Button.module.css';
function Button({ lable, onClick, className }: {
lable: string,
onClick?(): void,
}) {
return <div onClick={onClick} className={buttonStyle.Button}>
<p>{lable}</p>
</div>
}
Button.HoverContext = buttonStyle.HoverContext;
export default Button;
For better reusability and code structure, we can wrap the functionality in a Higher-Order Component.
import * as React from 'react'
import clsx from 'clsx';
import buttonStyle from './Button.module.css';
function Button({ lable, onClick }: {
lable: string,
onClick?(): void,
}) {
return <div onClick={onClick} className={buttonStyle.Button}>
<p>{lable}</p>
</div>
}
function HoverContext({ children }: { children: React.ReactElement<{ className: string }> }) {
const originalProps = children.props;
return React.cloneElement(
children,
{
...originalProps,
className: clsx(originalProps.className, buttonStyle.HoverContext)
}
);
}
Button.HoverContext = HoverContext;
export default Button;
Usage Examples
On the consumer side, user can choose to opt-in the HoverContext when needed.
<Button label="Click me" />
// If exporting the className
<div className={Button.HoverContext}>
<Button label="Hover parent to see effect" />
</div>
// Using the HOC approach
<Button.HoverContext>
<Card>
<Button label="Interactive card button" />
<Card/>
</Button.HoverContext>
By doing so, we achieved:
- Single Source of Truth: All button related style information in one file.
- Better Encapsulation: Button component contain the hover effect styling information in itself.
- Explicit opt-in: My favorite aspect, consumers choose when to enable it.
Ending
We pushed the Compound Pattern to a bit further in this blog, which put forward an elegant solution for context-dependent styling in React. By providing explicit opt-in mechanisms for style behaviors, we maintain component independence while enabling rich interactions between components.
This pattern works particularly well for design systems where you need consistent, composable behaviors across many components without tight coupling.
Related
Convincing Myself to "Vibe Code"
by a Developer
Dec 2025
by a Developer
ai
machine-learning
To vibe or not to vibe, that's the question. Draw the line between throwaway AI vibe coded prototypes and sustainable software engineering without losing your edge.
Code like an Onion: The Decorator Pattern
by a Developer
Jul 2025
by a Developer
design-pattern
python
An introduction to the Decorator Design Pattern, which helps manage cross-cutting concerns by wrapping your core functionality like an onion🧅.
The CSS Odyssey: Why I Turned back to CSS After Trying Everything Else
by a Developer
Jun 2025
by a Developer
css
react
web-dev
A developer's CSS journey: from vanilla to CSS-in-JS to Tailwind and back to CSS, with hard-earned lessons along the way.
5 WTF Moments in Python
by a Developer
May 2025
by a Developer
python
This isn't another Python beginner guide or critique of Python's performance issues. Instead, the blog walks through five surprising behaviors in Python that have bitten me in the past, knowing these might save you hours of debugging.