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 AI am not a .card.card B.card CThe
.breakis 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:
oddoreven. - A formula:
An + Blike3n + 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;
}
123456789101112
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;
}
NameRoleLevelAdaDesigner3LionelEngineer4GraceEngineer5MargaretPM4
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;
}
123456789101112
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;
}
- Step 1: Setup
- Step 2: Basics
- Step 3: Styling
- Step 4: Interactions
- Step 5: Polish
- 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:
- Look at the parent’s children.
- Keep only the children that match
.class(or whatever selector list you pass). - 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
.featuredclass”, even if it’s the 9th child overall. -
You can target “the last
.warningrow” 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 normalItem 2 vipItem 3 normalItem 4 specialItem 5 normalItem 6 vipItem 7 normalItem 8 vipTry 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:.vipand.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.
