CSS Position Explained: static, relative, absolute, fixed and sticky
The five CSS position values and what each does: static (normal flow), relative (nudge and anchor), absolute (place inside the nearest positioned ancestor), fixed (stick to the screen) and sticky (stick while scrolling). With examples and the reasons sticky and absolute 'don't work'.
The CSS position property decides how an element is placed on the page. There are five values, and most layout confusion comes from mixing up relative and absolute.
static — the default
.box { position: static; }
The element sits in the normal flow of the page: one after another, top to bottom. top, left, right, bottom and z-index have no effect. You almost never write static explicitly.
relative — nudge it, and make it an anchor
.box { position: relative; top: 10px; left: 20px; }
The element stays in the normal flow (its space is kept), but you can nudge it from where it would have been. Moving things this way is rare.
The real reason people use position: relative is the second effect: it becomes the reference point for absolutely positioned children.
absolute — place it inside the nearest positioned ancestor
.card { position: relative; }
.badge { position: absolute; top: 8px; right: 8px; }
<div class="card">
<span class="badge">New</span>
...
</div>
An absolutely positioned element:
- is removed from the normal flow — other elements act as if it isn't there,
- is placed relative to its nearest ancestor that has a position other than static.
That's why the pattern is always "parent relative, child absolute". If no ancestor is positioned, the element is placed relative to the whole page — which is why a badge sometimes flies to the top corner of the screen.
fixed — stuck to the screen
.chat-button { position: fixed; bottom: 24px; right: 24px; }
Placed relative to the browser window, and stays there when you scroll. Used for floating buttons, cookie banners and some headers.
Gotcha: if any ancestor has a transform, filter or perspective set, fixed behaves like absolute relative to that ancestor instead. If your fixed element scrolls away, check the ancestors.
sticky — normal until you scroll past it
.table-header { position: sticky; top: 0; }
Behaves like relative until scrolling would take it off-screen, then sticks at the top value — until its parent container scrolls away. Perfect for table headers, section headings and sidebars.
"Sticky isn't working"
The usual causes:
- No
top(orbottom) value. Sticky needs to know where to stick. - An ancestor has
overflow: hidden,autoorscroll. Sticky sticks within the nearest scrolling ancestor;overflow: hiddenon a parent breaks it. - The parent is only as tall as the sticky element, so there's no room to stick.
- It's inside a flex/grid container stretching it — add
align-self: start.
Quick reference
| Value | In normal flow? | Positioned relative to | Typical use |
|---|---|---|---|
static |
Yes | — | Default |
relative |
Yes | Its own normal spot | Anchor for absolute children |
absolute |
No | Nearest positioned ancestor | Badges, dropdowns, overlays |
fixed |
No | The viewport | Floating buttons, banners |
sticky |
Yes | Scroll container | Sticky headers |
Layering: z-index
Positioned elements can overlap. z-index controls which is on top — but only works on positioned elements and within "stacking contexts". If z-index seems to be ignored, see z-index not working.
Do you need position at all?
For page layout — columns, centring, spacing — use Flexbox or Grid, not position. Positioning is for elements that need to break out of the layout: overlays, badges, sticky bits. (Flexbox vs Grid, How to center a div)
EasySpawn runs your app on a server with a live preview, and Claude Code can fix a misbehaving layout while you watch the page update. See how it works or join the waitlist.
Related: z-index Not Working? · Flexbox vs Grid · How to Center a Div · Responsive Design Basics
Keep reading
"ReferenceError: window is not defined" in Next.js: How to Fix It
window, document and localStorage only exist in the browser. Next.js renders components on the server first, where they don't exist. Why it happens, and the fixes: useEffect, 'use client', typeof checks, dynamic imports with ssr: false, and libraries that touch window on import.
What Is TypeScript? JavaScript With Labels, Explained for Beginners
TypeScript is JavaScript plus types: labels that say what kind of value each thing holds, checked before your code runs. What it looks like, why AI tools default to it, what .ts and .tsx files are, and how to read the red squiggles.