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>
)

Resources


‹ Documentation