Show or hide an element by parent class in Tailwind
By Flavio
Published · Updated
Tagged: CSS
Learn a lesser-known Tailwind trick that shows or hides an element based on a parent class, using the group utility with the group-[.is-published] variant.
Little relatively unknown Tailwind trick using group:
<div class="group is-published">
<div class="hidden group-[.is-published]:block">
Show only when parent has class "is-published"
</div>
</div>
The inner div is hidden by default. It shows up only when the parent has the is-published class.
How it works
group is a marker class. It adds no styles, it only tells Tailwind “this is the parent I want to look at”.
group-[...] takes any CSS selector between the square brackets. The utility after the colon applies when the element with the group class matches that selector.
This is the CSS Tailwind generates:
.group-\[\.is-published\]\:block:is(:where(.group):is(.is-published) *) {
display: block;
}
You can read 2 things from it.
is-published must be on the same element that has group. If you put group on the parent and is-published on the grandparent, nothing happens.
The element can be at any depth inside the group. It does not have to be a direct child.
Toggle the class with JavaScript
Here is an example you can paste in a page that uses Tailwind:
<article id="post" class="group">
<span class="hidden group-[.is-published]:inline">Published</span>
<span class="group-[.is-published]:hidden">Draft</span>
<button id="toggle">Toggle</button>
</article>
<script>
document.querySelector('#toggle').addEventListener('click', () => {
document.querySelector('#post').classList.toggle('is-published')
})
</script>
The first span is the trick from before.
The second one does the opposite. It’s visible by default, and group-[.is-published]:hidden hides it when the parent gets the class.
You can also write the “parent does not have the class” case with :not():
<span class="hidden group-[:not(.is-published)]:inline">Draft</span>
Skip the group class with in-*
Tailwind 4 added the in-* variant. It works like group, but you don’t add any class to the parent:
<div class="is-published">
<div class="hidden in-[.is-published]:block">
Show only when a parent has class "is-published"
</div>
</div>
The difference is that in-* looks at every ancestor, at any level. If any element above this one has is-published, the div is shown.
That’s fine most of the time. When you want to react to one specific parent, use group.
Nested groups
If you nest elements with group, an inner element reacts to all of them. In this example the badge shows up, because one of the groups above it has the class:
<article class="group is-published">
<div class="group">
<span class="hidden group-[.is-published]:inline">Approved</span>
</div>
</article>
To look at one group only, give each group a name with group/name, and add the same name after the brackets:
<article class="group/post is-published">
<div class="group/comment">
<span class="hidden group-[.is-published]/comment:inline">Approved</span>
</div>
</article>
Now the badge stays hidden. It only looks at group/comment, and that element does not have the class.
You can pick any name. There is nothing to configure.
Use a data attribute instead of a class
If the state lives in a data-* attribute, Tailwind has a variant for that too:
<div class="group" data-state="published">
<div class="hidden group-data-[state=published]:block">Published</div>
</div>
In Tailwind 4 you can also check that an attribute is there, without a value:
<div class="group" data-published>
<div class="hidden group-data-published:block">Published</div>
</div>
If it does not work
Check these things:
- the parent has both
groupand the class you are looking for, on the same element - the class name is written in full in your source. Tailwind scans your files for complete class names, so a class built at runtime like
group-[.${state}]:blockis never generated. I wrote about this in you can’t generate classes dynamically in Tailwind - you nested groups without naming them, and the element reacts to a group you didn’t expect
Want me to talk about your product? You can sponsor this site.