Using the group className
March 22, 2026 · View on GitHub
There’s a couple of Tailwind classes that need to be added to React elements as a className.
These classes are the peer and the group classes.
A className is used so variants like group-hover: and peer-hover: can use the className as an anchor to allow their styles to work.
Here’s how we use the group classes in twin:
import 'twin.macro'
export default () => (
<button className="group">
<div tw="group-hover:bg-black">Child 1</div>
<div tw="group-hover:font-bold">Child 2</div>
</button>
)
When working in emotion and styled-components without the group classes, the equivalent looks like this:
import tw, { styled } from 'twin.macro'
const Group = tw.button``
Group.Child1 = styled.div`
${Group}:hover & {
${tw`bg-black`}
}
`
Group.Child2 = styled.div`
${Group}:hover & {
${tw`font-bold`}
}
`
export default () => (
<Group>
<Group.Child1>Child 1</Group.Child1>
<Group.Child2>Child 2</Group.Child2>
</Group>
)
Not as great right?
Here’s some ways you can improve upon that:
Attrs in 💅 styled‑components
In styled-components we have a styled function called attrs.
Here’s what the docs have to say about it:
The rule of thumb is to use attrs when you want every instance of a styled component to have that prop, and pass props directly when every instance needs a different one.
- styled-components docs
But we can also put it to use to define the group class in Tailwind.
Rather than adding className="group" directly onto your jsx element, the class can be more tightly coupled with your styles:
import tw, { styled } from 'twin.macro'
const Group = styled.button.attrs({ className: 'group' })``
Group.Child1 = tw.div`group-hover:bg-black`
Group.Child2 = tw.div`group-hover:font-bold`
export default () => (
<Group>
<Group.Child1>Child 1</Group.Child1>
<Group.Child2>Child 2</Group.Child2>
</Group>
)
Attrs in emotion
Unfortunately emotion doesn’t have any plans to add attrs so the easiest option is to add className="group" directly on the jsx element:
import tw from 'twin.macro'
const Group = tw.button``
Group.Child1 = tw.div`group-hover:bg-black`
Group.Child2 = tw.div`group-hover:font-bold`
export default () => (
<Group className="group">
<Group.Child1>Child 1</Group.Child1>
<Group.Child2>Child 2</Group.Child2>
</Group>
)
But if you’d like similar functionality to the attr function in styled-components then you could add the className using a Higher-Order Component (HOC):
import tw from 'twin.macro'
const withAttrs = (Component, attrs) => props =>
<Component {...attrs} {...props} />
const Button = tw.button``
const Group = withAttrs(Button, { className: 'group' })
Group.Child1 = tw.div`group-hover:bg-black`
Group.Child2 = tw.div`group-hover:font-bold`
export default () => (
<Group>
<Group.Child1>Child 1</Group.Child1>
<Group.Child2>Child 2</Group.Child2>
</Group>
)
You could also use defaultProps but it’s deprecated in React 18.3+ and removed in React 19:
import tw from 'twin.macro'
const Group = tw.button``
Group.defaultProps = { className: 'group' }
Group.Child1 = tw.div`group-hover:bg-black`
Group.Child2 = tw.div`group-hover:font-bold`
export default () => (
<Group>
<Group.Child1>Child 1</Group.Child1>
<Group.Child2>Child 2</Group.Child2>
</Group>
)