I'm just getting started with emotion after working with react-jss for a few years. One thing I'm struggling to find a good example of is passing (some, possibly dynamic) styles to a child component. In react-jss
this was pretty trivial:
// In SomeParent.jsx
... imports etc ...
const useStyles = createUseStyles({
custom: {
color: 'blue',
},
})
export const CustomBody = () => {
const classes = useStyles()
return <Body className={classes.custom}>some text</Body>
}
// In Body.jsx
... imports etc...
const useStyles = createUseStyles({
body: {
fontSize: 16,
color: 'black',
},
})
const Body = ({ children, className }) => {
const classes = useStyles()
return (
<p
className={clsx(
classes.body,
className,
)}
>
{children}
</p>
)
}
Essentially, parent components could create styles and pass any number of related class names via a prop (usually className
) to the child, which could then consume them in their own className
prop, combining them with any other styles the child component needs.
Emotion definitely does allow this same thing, but I can't figure out how they expect you to do it.
Semantically on the child-side, it seems like the styled tagged template literal takes in emotion
styles and passes a className
prop to the child, which then would work the same way it does in react-jss. However, I find the styled
syntax to be crazy ugly and hard to parse. I'd rather not fill my parent files up with potentially differently styled
HOC-wrapped child components.
Functionally, it seems like passing my own prop of emotion
styles and composing those styles in the child component most closely mirrors react-jss
, but I can't find any examples in the emotion documentation suggesting that this is something they expect you to do. It's not FORBIDDEN or anything, it's just not highlighted. Does something like the following make sense?
// In SomeParent.jsx
... imports etc ...
export const CustomBody = () => {
return <Body customStyles={css`color: blue`}>some text</Body>
}
// In Body.jsx
... imports etc...
const bodyStyles = css`
font-size: 16px;
color: black;
`
const Body = ({ children, customStyles }) => {
return (
<p
css={css`
${bodyStyles};
${customStyles};
`}
>
{children}
</p>
)
}
The composition documentation makes clear that this will work (and it does), but it doesn't specifically showcase passing styles from one component to another the way the styled
documentation does. This feels better to me, but I am worried about the note in the string styles documentation which points out that css
doesn't return a class string, but instead returns a full object that is "understood by emotion at a low level". If this object isn't memoized by emotion
(I haven't tested this myself yet) then I'm going to end up with a bunch of unnecessary re-renders. I could memoize on the class name string that is included as part of that object, but I'm not sure how consistent that is either.
The last thing that might be relevant is the class names API. It looks like this would allow me to create a style and pass it as a className
string to a child component (much like in react-jss
and avoiding the re-render problem), but again, having to wrap EVERY child component that accepts a style from a parent in the ClassNames
component and then dealing with render functions seems crazy.
Am I just missing something here? Has the whole world accepted that styled
and the styled component pattern is just the way to go with this stuff? Please let me know how you've solved this problem, or point me to the documentation that I've over looked. Thanks a lot!
In case anyone finds this post later, I ran my own tests comparing the performance of each of these methods when creating 1000 p tags and coloring them based on their index.
styled
was the most performant,ClassNames
was second most performant (with its best performance generally matching the lower end ofstyled
's performance). Passing thecss
tagged template literal directly was much worse than either of the other two, with performance 1.5x to 2x worse thanstyled
's worst performance.Conditionally styling by passing props is absolutely slower than static styles, and using the prop filtering for
styled
only decreases performance a small amount over not filtering (probably a good tradeoff to prevent unnecessary re-renders if that's a potential issue).