CSS's :nth-child() pseudo-class is one of those features that feels like a superpower once it clicks. You can stripe table rows, give the first three cards a special treatment, skip every other item in a grid — all without adding a class manually or reaching for JavaScript. Tailwind gives you access to all of it, partly through named shortcuts and partly through its arbitrary variant system.
This guide covers the full picture: the named variants you should use by default, the arbitrary variant syntax for more complex expressions, and practical patterns you'll reach for on real projects.
The named shortcuts: first, last, odd, even, only
For the most common cases, Tailwind ships dedicated variant prefixes that map directly to CSS pseudo-classes. You don't need brackets or custom configuration — just prefix any utility class:
first:— targets:first-childlast:— targets:last-childodd:— targets:nth-child(odd)even:— targets:nth-child(even)only:— targets:only-child
A classic use case: a list where every item has a top border, but not the first one.
<ul>
<li class="py-4 border-t first:border-t-0">Item one</li>
<li class="py-4 border-t first:border-t-0">Item two</li>
<li class="py-4 border-t first:border-t-0">Item three</li>
</ul>
The same pattern works for removing bottom padding on the last item in a card, or striping table rows without a JavaScript counter:
<tbody>
<tr class="odd:bg-white even:bg-gray-50">
<td>Alice</td>
<td>[email protected]</td>
</tr>
<tr class="odd:bg-white even:bg-gray-50">
<td>Bob</td>
<td>[email protected]</td>
</tr>
</tbody>
These compose cleanly with responsive and dark-mode variants too — dark:odd:bg-gray-900/50 is perfectly valid.
Targeting a specific position: nth-[n]
Tailwind v4 introduced first-class nth- variants that accept a number directly. To highlight the third child, you write nth-3:. No angle brackets, no configuration:
<ul>
<li class="text-gray-600 nth-3:font-bold nth-3:text-blue-600">Item one</li>
<li class="text-gray-600 nth-3:font-bold nth-3:text-blue-600">Item two</li>
<li class="text-gray-600 nth-3:font-bold nth-3:text-blue-600">Item three</li>
<li class="text-gray-600 nth-3:font-bold nth-3:text-blue-600">Item four</li>
</ul>
The same family covers counting from the end and type-aware targeting:
nth-3:— third child (:nth-child(3))nth-last-3:— third from the end (:nth-last-child(3))nth-of-type-2:— second of its element type (:nth-of-type(2))nth-last-of-type-2:— second of its type from the end
The -of-type variants are particularly useful when mixed element types share a parent — for example, a container with both headings and paragraphs where you only want to style the paragraphs.
Arbitrary expressions: nth-[An+B]
When you need a repeating pattern or a range, you'll use Tailwind's arbitrary variant syntax. Wrap the CSS expression in square brackets, using underscores where CSS expects spaces:
<!-- Every third item -->
<li class="[&:nth-child(3n)]:opacity-50">...</li>
<!-- First three items only -->
<li class="[&:nth-child(-n+3)]:ring-2 [&:nth-child(-n+3)]:ring-blue-500">...</li>
<!-- From the fourth item onward -->
<li class="[&:nth-child(n+4)]:hidden">...</li>
The & represents the element itself — it's the same token Tailwind uses throughout its arbitrary variant system. The selector [&:nth-child(3n)] compiles to .your-class:nth-child(3n), which is exactly the CSS you'd write by hand.
Tailwind v4 also supports the more expressive of <selector> syntax that browsers added to :nth-child(). To target every other <li> specifically (not every other child regardless of type), use:
<li class="nth-[2n+1_of_li]:bg-gray-100">...</li>
The underscore before of represents the space character in the CSS expression, consistent with how Tailwind handles spaces throughout its arbitrary value system.
Practical patterns for real layouts
Feature card grid: highlight the lead card
A common marketing pattern puts the first card in a grid at full width, with the rest in a multi-column row below it. You can do this purely with positional variants:
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<article class="rounded-xl border p-6 first:lg:col-span-3 first:lg:flex first:lg:gap-8">
<h2>Primary Feature</h2>
<p>This card spans the full width on large screens.</p>
</article>
<article class="rounded-xl border p-6">Secondary</article>
<article class="rounded-xl border p-6">Secondary</article>
</ul>
Truncating a long list with a "show more" pattern
If you render a full list server-side but want to hide items beyond a threshold by default, nth-child lets you do it without JavaScript on first load:
<ul>
<!-- Show first 5; hide the rest -->
<li class="[&:nth-child(n+6)]:hidden">Item</li>
<li class="[&:nth-child(n+6)]:hidden">Item</li>
<li class="[&:nth-child(n+6)]:hidden"gt;Item</li>
<!-- ... -->
</ul>
Wire up a toggle button with a class swap or Alpine.js to remove the hidden override when the user clicks "Show more."
Diagonal grid accents
Zebra striping is familiar in tables, but the same principle applies to card grids. Alternating a subtle background on odd-numbered cards adds visual rhythm without borders:
<div class="grid grid-cols-2 gap-4">
<div class="p-6 rounded-lg odd:bg-gray-50 even:bg-white">Card A</div>
<div class="p-6 rounded-lg odd:bg-gray-50 even:bg-white">Card B</div>
<div class="p-6 rounded-lg odd:bg-gray-50 even:bg-white">Card C</div>
<div class="p-6 rounded-lg odd:bg-gray-50 even:bg-white">Card D</div>
</div>
Pricing tier emphasis
With a three-column pricing layout, you often want the middle column to stand out. Target it directly:
<div class="grid grid-cols-3 gap-6">
<div class="rounded-xl border p-8 nth-2:border-blue-500 nth-2:shadow-xl nth-2:scale-105">
Starter
</div>
<div class="rounded-xl border p-8 nth-2:border-blue-500 nth-2:shadow-xl nth-2:scale-105">
Pro <!-- this one gets the ring and scale -->
</div>
<div class="rounded-xl border p-8 nth-2:border-blue-500 nth-2:shadow-xl nth-2:scale-105">
Agency
</div>
</div>
Combining nth with other variants
Positional variants stack with responsive breakpoints, hover states, and dark mode exactly like any other Tailwind variant. The order is: responsive prefix, then state, then your utility.
<!-- Underline even items on hover, but only at medium breakpoints and up -->
<li class="md:even:hover:underline">...</li>
<!-- Dark mode zebra striping -->
<tr class="odd:bg-white even:bg-gray-50 dark:odd:bg-gray-900 dark:even:bg-gray-800">
...
</tr>
The same applies to arbitrary variants — they're just another modifier in the chain:
<li class="lg:[&:nth-child(-n+3)]:font-semibold">...</li>
When nth-child doesn't do what you expect
A handful of gotchas catch people out the first time.
Mixed element types shift the count. :nth-child(2) selects the second child of the parent — regardless of what element type it is. If your parent has an <h2> followed by <p> elements, the <h2> is child 1. Use :nth-of-type() (or Tailwind's nth-of-type-[n]:) when you want to count only elements of the same type.
The of <selector> syntax is newer. The nth-[2n+1_of_li] form is supported in all modern browsers, but if you're targeting older environments check your support matrix. The plain :nth-child(An+B) form has been universally supported for years.
Arbitrary variants apply the selector to the element that carries the class. [&:nth-child(3)] on a <li> means "this <li>, when it is the third child." If the class is on the <ul> instead, the selector won't match the way you intend. Keep the class on the element you're targeting, not its parent.
Quick reference
first:/last:/only:— first, last, or only childodd:/even:— alternating childrennth-3:— specific position (v4 shorthand)nth-last-3:— specific position from the endnth-of-type-2:— specific position, same type only[&:nth-child(3n)]:— every third child (arbitrary)[&:nth-child(-n+3)]:— first three children (arbitrary)[&:nth-child(n+4)]:— fourth child and beyond (arbitrary)nth-[2n+1_of_li]:— expression with type filter (v4)
Positional variants remove a whole category of micro-JavaScript from typical UI work. Stripe a table, truncate a list, highlight a featured card — if the logic depends only on where an element sits in its parent, a Tailwind nth variant is usually the right tool. Start with the named shortcuts (first:, odd:), step up to nth-3: when you need a specific index, and reach for [&:nth-child()] arbitrary variants only when the expression is genuinely complex.