CSS :nth-child() Introduction

The :nth-child() pseudo-class lets you select elements based on their position among siblings. It’s one of those selectors that feels like magic… until you remember it’s just counting.

Key idea: :nth-child() counts elements, not classes. It looks at where an element sits in its parent, then checks whether that position matches the rule you wrote.

.list > li:nth-child(2) {
  outline: 3px solid #111;
  background: #fff7d6;
}
  
.list > li:nth-child(4) {
  outline: 3px solid #111;
  background: #d6fff3;
}
  
.list > li:nth-child(7) {
  outline: 3px solid #111;
  background: #e8ddff;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 920px;
  padding: 18px;
  display: grid;
  gap: 14px;
  font-family: system-ui, Arial, sans-serif;
}

.title {
  margin: 0;
  font-weight: 800;
}

.list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 10px;
}

.list > li {
  padding: 12px 14px;
  border: 2px solid #111;
  border-radius: 14px;
  background: #fff;
}
  

We are selecting an exact position

  • Item 1
  • Item 2 (watch me)
  • Item 3
  • Item 4 (watch me)
  • Item 5
  • Item 6
  • Item 7 (watch me)
  • Item 8

What :nth-child() Actually Counts

:nth-child() counts every element node that is a child of the same parent. It does not care about classes, names, or your hopes and dreams.

If the parent contains h3, p, and div siblings, those all count as children positions too. Then your selector is checked against that position.

.stack > .card:nth-child(2) {
  outline: 3px solid #111;
  background: #fff7d6;
}
  
.stack > .card:nth-child(3) {
  outline: 3px solid #111;
  background: #d6fff3;
}
  
.stack > .card:nth-child(4) {
  outline: 3px solid #111;
  background: #e8ddff;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 920px;
  padding: 18px;
  display: grid;
  gap: 14px;
  font-family: system-ui, Arial, sans-serif;
}

.stack {
  display: grid;
  gap: 10px;
  padding: 14px;
  border: 3px solid #111;
  border-radius: 18px;
  background: #f6f6f6;
}

.card,
.break {
  padding: 12px 14px;
  border: 2px solid #111;
  border-radius: 14px;
  background: #fff;
}

.break {
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.note {
  font-size: 14px;
  opacity: 0.85;
}
  
.card A
I am not a .card
.card B
.card C

The .break is the second child, so .card B becomes the third child. This is the #1 reason people say “nth-child is not working”.

CSS :nth-child() Selector Syntax

You can pass a few kinds of values into :nth-child():

  • A number: :nth-child(4) means “the 4th child”.
  • Keywords: odd or even.
  • A formula: An + B like 3n + 1.

The An + B Formula (No, It’s Not Scary)

Think of n as a counter that can be 0, 1, 2, 3.... A formula like 3n matches every 3rd item: 3, 6, 9....

  • 3n → 3, 6, 9, 12...
  • 3n + 1 → 1, 4, 7, 10...
  • 3n + 2 → 2, 5, 8, 11...
.grid > .cell:nth-child(3n) {
  background: #fff7d6;
  outline: 3px solid #111;
}
  
.grid > .cell:nth-child(3n + 1) {
  background: #d6fff3;
  outline: 3px solid #111;
}
  
.grid > .cell:nth-child(3n + 2) {
  background: #e8ddff;
  outline: 3px solid #111;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 920px;
  padding: 18px;
  display: grid;
  gap: 14px;
  font-family: system-ui, Arial, sans-serif;
}

.grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 10px;
  padding: 14px;
  border: 3px solid #111;
  border-radius: 18px;
  background: #f6f6f6;
}

.cell {
  border: 2px solid #111;
  border-radius: 14px;
  background: #fff;
  padding: 12px 10px;
  text-align: center;
  font-weight: 800;
}
  
1
2
3
4
5
6
7
8
9
10
11
12

CSS :nth-child() First and Last Patterns

There is no :nth-child(first) keyword, but you can still do “first” and “last” easily.

CSS nth child first

The first child is simply :nth-child(1). (Yes, counting starts at 1, not 0.)

CSS nth child last

For the last child, you usually use :last-child. But you can also do “count from the end” with :nth-last-child().

.menu > a:nth-child(1) {
  background: #d6fff3;
  outline: 3px solid #111;
}
  
.menu > a:last-child {
  background: #fff7d6;
  outline: 3px solid #111;
}
  
.menu > a:nth-last-child(2) {
  background: #e8ddff;
  outline: 3px solid #111;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 920px;
  padding: 18px;
  display: grid;
  gap: 14px;
  font-family: system-ui, Arial, sans-serif;
}

.menu {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 14px;
  border: 3px solid #111;
  border-radius: 18px;
  background: #f6f6f6;
}

.menu > a {
  text-decoration: none;
  color: #111;
  font-weight: 800;
  border: 2px solid #111;
  border-radius: 999px;
  padding: 10px 14px;
  background: #fff;
}
  

  

Learn more about :first-child and :last-child in the CSS First Child Interactive Tutorial and the CSS Last Child Interactive Tutorial.

CSS nth child odd and even

odd and even are friendly shortcuts:

  • :nth-child(odd) matches 1, 3, 5, 7…
  • :nth-child(even) matches 2, 4, 6, 8…

Perfect for zebra-striping lists and tables.

.table .row:nth-child(odd) {
  background: #fff7d6;
}
  
.table .row:nth-child(even) {
  background: #d6fff3;
}
  
.table .row:nth-child(odd) .cell:first-child {
  font-weight: 900;
  text-decoration: underline;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 920px;
  padding: 18px;
  display: grid;
  gap: 14px;
  font-family: system-ui, Arial, sans-serif;
}

.table {
  border: 3px solid #111;
  border-radius: 18px;
  overflow: hidden;
  background: #fff;
}

.row {
  display: grid;
  grid-template-columns: 1.2fr 1fr 1fr;
}

.cell {
  padding: 12px 14px;
  border-bottom: 2px solid #111;
}

.row:last-child .cell {
  border-bottom: 0;
}

.header {
  background: #111;
  color: #fff;
  font-weight: 900;
}

.header .cell {
  border-bottom: 2px solid #111;
}
  
Name
Role
Level
Ada
Designer
3
Lionel
Engineer
4
Grace
Engineer
5
Margaret
PM
4

CSS nth child multiple values

Sometimes you want “these specific positions” without doing math. You can combine selectors with commas.

Example: the first snippet highlights the 2nd, 4th, and 7th item:

.pills > button:nth-child(2),
.pills > button:nth-child(4),
.pills > button:nth-child(7) {
  background: #111;
  color: #fff;
}
  
.pills > button:nth-child(1),
.pills > button:nth-child(3),
.pills > button:nth-child(5) {
  background: #d6fff3;
}
  
.pills > button:nth-child(3n + 2),
.pills > button:nth-child(3n + 3) {
  background: #999;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 920px;
  padding: 18px;
  display: grid;
  gap: 14px;
  font-family: system-ui, Arial, sans-serif;
}

.pills {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 14px;
  border: 3px solid #111;
  border-radius: 18px;
  background: #f6f6f6;
}

.pills > button {
  border: 2px solid #111;
  border-radius: 999px;
  background: #fff;
  padding: 10px 14px;
  font-weight: 800;
  cursor: pointer;
}
  

CSS nth child range selectors

Ranges are where :nth-child() becomes really practical. You can select “first 5”, “from item 4 to 9”, “everything except the first 2”, and so on.

Selecting the first N children

Use -n + N. Example: :nth-child(-n + 5) matches 1 through 5.

Selecting from N onward

Use n + N. Example: :nth-child(n + 4) matches 4, 5, 6, 7...

Selecting between A and B

Combine two selectors: :nth-child(n + A):nth-child(-n + B). Example: :nth-child(n + 4):nth-child(-n + 9) matches 4 through 9.

.line > .dot:nth-child(-n + 5) {
  background: #d6fff3;
  outline: 3px solid #111;
}
  
.line > .dot:nth-child(n + 6) {
  background: #fff7d6;
  outline: 3px solid #111;
}
  
.line > .dot:nth-child(n + 4):nth-child(-n + 9) {
  background: #e8ddff;
  outline: 3px solid #111;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 920px;
  padding: 18px;
  display: grid;
  gap: 14px;
  font-family: system-ui, Arial, sans-serif;
}

.line {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 10px;
  padding: 14px;
  border: 3px solid #111;
  border-radius: 18px;
  background: #f6f6f6;
}

.dot {
  border: 2px solid #111;
  border-radius: 14px;
  background: #fff;
  height: 44px;
  display: grid;
  place-items: center;
  font-weight: 900;
}
  
1
2
3
4
5
6
7
8
9
10
11
12

CSS nth child of type

This is the classic confusion:

  • :nth-child() counts the element’s position among all children.
  • :nth-of-type() counts the element’s position among only the same tag name.

If your parent mixes p and div, :nth-child(2) and :nth-of-type(2) can target totally different elements.

.feed > p:nth-child(2) {
  outline: 3px solid #111;
  background: #fff7d6;
}
  
.feed > p:nth-of-type(2) {
  outline: 3px solid #111;
  background: #d6fff3;
}
  
.feed > div:nth-child(3) {
  outline: 3px solid #111;
  background: #e8ddff;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 920px;
  padding: 18px;
  display: grid;
  gap: 14px;
  font-family: system-ui, Arial, sans-serif;
}

.feed {
  display: grid;
  gap: 10px;
  padding: 14px;
  border: 3px solid #111;
  border-radius: 18px;
  background: #f6f6f6;
}

.feed > * {
  border: 2px solid #111;
  border-radius: 14px;
  background: #fff;
  padding: 12px 14px;
  margin: 0;
}
  

Paragraph 1

Divider (a div)

Paragraph 2

Paragraph 3

Why “nth-child is not working” happens

  • Wrong parent: your selector might be counting within a different container than you think.
  • Unexpected siblings: a spacer div, a heading, an icon wrapper… they all count.
  • Text nodes confusion: whitespace in HTML does not count as children for :nth-child() (it counts element children), but elements you forgot about absolutely do.
  • You wanted “nth-of-type”: you meant “the 2nd p”, not “the 2nd child”.

Real-World :nth-child() Patterns

Select every 3rd card (3, 6, 9...)

3n is your friend when you want “every 3rd item”. This is common in grids for special styling or spacing.

.cards > .card:nth-child(3n) {
  transform: translateY(-6px);
  background: #fff7d6;
}
  
.cards > .card:nth-child(3n + 1) {
  background: #d6fff3;
}
  
.cards > .card:nth-child(3n + 2) {
  background: #e8ddff;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 980px;
  padding: 18px;
  display: grid;
  gap: 14px;
  font-family: system-ui, Arial, sans-serif;
}

.cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  padding: 14px;
  border: 3px solid #111;
  border-radius: 18px;
  background: #f6f6f6;
}

.card {
  border: 2px solid #111;
  border-radius: 18px;
  background: #fff;
  padding: 14px;
  display: grid;
  gap: 10px;
}

.card strong {
  font-size: 18px;
}

.card p {
  margin: 0;
  opacity: 0.85;
  line-height: 1.4;
}
  
Card 1

One.

Card 2

Two.

Card 3

Three.

Card 4

Four.

Card 5

Five.

Card 6

Six.

Card 7

Seven.

Card 8

Eight.

Card 9

Nine.

Skip the first two items (style the rest)

This is a very common “range” use-case: :nth-child(n + 3) matches 3 and onward.

.steps > li:nth-child(n + 3) {
  background: #111;
  color: #fff;
}
  
.steps > li:nth-child(-n + 2) {
  background: #d6fff3;
  outline: 3px solid #111;
}
  
.steps > li:nth-child(n + 3):nth-child(-n + 5) {
  background: #fff7d6;
  outline: 3px solid #111;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 920px;
  padding: 18px;
  display: grid;
  gap: 14px;
  font-family: system-ui, Arial, sans-serif;
}

.steps {
  list-style: none;
  padding: 14px;
  margin: 0;
  display: grid;
  gap: 10px;
  border: 3px solid #111;
  border-radius: 18px;
  background: #f6f6f6;
}

.steps > li {
  border: 2px solid #111;
  border-radius: 14px;
  background: #fff;
  padding: 12px 14px;
  font-weight: 800;
}
  
  1. Step 1: Setup
  2. Step 2: Basics
  3. Step 3: Styling
  4. Step 4: Interactions
  5. Step 5: Polish
  6. Step 6: Ship it

Selecting “nth of a class” with :nth-child(N of .class)

If you’ve ever wished CSS had a :nth-of-class() selector, you’re not alone. There is no direct :nth-of-class in CSS. For a long time, the closest “built-in” option was :nth-of-type(), but that only filters by element type (like p vs div), not by class.

The good news: the modern versions of :nth-child() and :nth-last-child() accept an optional of S clause, where S is a selector list. This lets you filter the children to only those matching S, then apply the nth counting.

At the time of writing, browser support is around ~92%. For the most up-to-date support data, check Can I use: css-nth-child-of.

What “of S” means (the important mental model)

With :nth-child(N of .class), the browser does this:

  1. Look at the parent’s children.
  2. Keep only the children that match .class (or whatever selector list you pass).
  3. Now count within that filtered set.

Example: :nth-child(1 of .class) selects the first child among the children that have .class. Any non-.class children before it are ignored for counting purposes.

It’s basically :nth-of-type(), but for arbitrary selectors instead of only element type.

And :nth-last-child(N of .class) does the same, from the end

:nth-last-child(N of .class) filters to .class siblings, then counts from the end of that filtered set. So :nth-last-child(1 of .class) selects the last .class in the parent.

Why this feels like a superpower

  • You can target “the 2nd item with the .featured class”, even if it’s the 9th child overall.
  • You can target “the last .warning row” without adding extra wrapper classes.
  • You can pass a selector list like :nth-child(2 of .vip, .special) to count across multiple groups.
.feed > .item.vip:nth-child(2) {
  outline: 3px solid #111;
  background: #fff7d6;
}
  
.feed > .item:nth-child(2 of .vip) {
  outline: 3px solid #111;
  background: #d6fff3;
}
  
.feed > .item:nth-last-child(1 of .vip) {
  outline: 3px solid #111;
  background: #e8ddff;
}
  
.feed > .item:nth-child(2 of .vip, .special) {
  outline: 3px solid #111;
  background: #111;
  color: #fff;
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 980px;
  padding: 18px;
  display: grid;
  gap: 14px;
  font-family: system-ui, Arial, sans-serif;
}

.title {
  margin: 0;
  font-weight: 900;
  font-size: 18px;
}

.feed {
  display: grid;
  gap: 10px;
  padding: 14px;
  border: 3px solid #111;
  border-radius: 18px;
  background: #f6f6f6;
}

.item {
  border: 2px solid #111;
  border-radius: 14px;
  background: #fff;
  padding: 12px 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.badges {
  display: inline-flex;
  gap: 8px;
}

.badge {
  border: 2px solid #111;
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 900;
  background: #fff;
}

.badge.vip {
  background: #ffe7a6;
}

.badge.special {
  background: #d6fff3;
}

.note {
  margin: 0;
  font-size: 14px;
  opacity: 0.85;
  line-height: 1.45;
}
  

Interleaved children: “VIP” items are not consecutive

Item 1 normal
Item 2 vip
Item 3 normal
Item 4 special
Item 5 normal
Item 6 vip
Item 7 normal
Item 8 vip

Try the snippets: Snippet 1 is the “old wishful thinking” version (2nd child overall). Snippet 2 uses :nth-child(2 of .vip) (2nd VIP). Snippet 3 uses :nth-last-child(1 of .vip) (last VIP). Snippet 4 counts across a selector list: .vip and .special.

Learn more about :nth-child(N of S) and :nth-last-child(N of S) in the CSS Nth Child (N of Selector) Interactive Tutorial.

CSS :nth-child() Cheat Sheet

  • :nth-child(1) → first child
  • :last-child → last child
  • :nth-child(odd) → 1, 3, 5…
  • :nth-child(even) → 2, 4, 6…
  • :nth-child(3n) → every 3rd (3, 6, 9…)
  • :nth-child(3n + 1) → 1, 4, 7…
  • :nth-child(-n + 5) → first 5
  • :nth-child(n + 4) → from 4 onward
  • :nth-child(n + 4):nth-child(-n + 9) → 4 through 9
  • :nth-last-child(2) → second from the end

Tips, Gotchas, and Best Practices

Always scope to the parent you mean

Prefer .parent > .child:nth-child(...) when you can. It keeps counting predictable and avoids “why is this styling weird stuff” moments.

Use :nth-of-type() when your siblings are mixed

If the parent contains multiple element types (like p and div), and your brain is thinking “the 2nd paragraph”, you want :nth-of-type().

Debugging checklist

  • Am I selecting the correct parent?
  • Are there other sibling elements affecting the count?
  • Did I mean :nth-of-type() instead of :nth-child()?
  • Is my selector too broad (missing >)?

Next Steps

If you’re feeling confident, the natural sequel is: :nth-last-child(), :first-of-type, :only-child, and combining selectors with :not() for “everything except…” patterns.

And yes, once you start using range selectors like -n + 5, you’ll begin to see the matrix.