All three units (px, em, and rem) set sizes in CSS. They look interchangeable until something breaks: text that does not scale when a user zooms, nested components where font sizes compound unexpectedly, or a design system that falls apart when the root font size changes.

This guide is not about how each unit works in isolation. It is about the decision: when to reach for each one, what goes wrong when you pick the wrong one, and the practical rules that experienced developers follow.

The quick reference

UnitRelative toScales with user zoom?Primary use
pxScreen pixels (fixed)PartiallyBorders, shadows, breakpoints
emParent element font-sizeYesComponent-relative spacing
remRoot <html> font-sizeYesFont sizes, consistent spacing

When to use px

Use px when you need something to stay a fixed, precise size regardless of font settings or context. Common cases:

  • Bordersborder: 1px solid should always be exactly one pixel. A border that scaled with font size would look wrong at larger text sizes.
  • Box shadows and outlines — Shadow spread and blur are visual decorations. They should not change because a user has increased their base font size.
  • Media query breakpoints — Layout breakpoints are about screen dimensions, not font size. Use px in @media queries.
  • Images and icons with fixed dimensions — When an image must be exactly 40×40px regardless of surroundings.
  • Absolute positioned overlays — Tooltips, dropdowns, and modals that need to be pixel-precise relative to the viewport.
/* Good uses of px */
.card {
  border: 1px solid #2d3148;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}

@media (max-width: 768px) {
  /* breakpoints always in px */
}

.icon {
  width: 24px;
  height: 24px;
}

Do not use px for font sizes. When users set a larger default font size in their browser for accessibility reasons, px font sizes ignore that preference entirely. This is a real accessibility failure, not a theoretical one.

When to use rem

Use rem for font sizes and most spacing values. Because rem is always relative to the root <html> font size (typically 16px by default), it gives you two important properties:

  • It scales consistently when a user changes their browser's default font size
  • It stays predictable no matter how deeply nested an element is in the DOM
/* Root font size — the base everything is relative to */
html {
  font-size: 16px; /* 1rem = 16px */
}

/* Font sizes in rem — scale with user preferences */
body        { font-size: 1rem;    } /* 16px */
h1          { font-size: 2.5rem;  } /* 40px */
h2          { font-size: 1.75rem; } /* 28px */
h3          { font-size: 1.25rem; } /* 20px */
small       { font-size: 0.875rem;} /* 14px */

/* Spacing in rem — consistent regardless of nesting */
.section    { padding: 3rem 1.5rem; }
.card       { padding: 1.5rem; }
.stack > * + * { margin-top: 1rem; }

The 62.5% trick: Some developers set html { font-size: 62.5%; } so that 1rem = 10px, making mental arithmetic easier. This is a valid pattern but requires setting body { font-size: 1.6rem; } to restore the default body size. Use it only if it helps your workflow.

When to use em

em is relative to the font size of the element it is applied to — or the parent element if used on font-size itself. This context-sensitivity makes it the right choice for spacing that should scale proportionally with the text size of the component it belongs to.

The most practical use case is padding and margin on components that have their own font size set:

/* Button padding that scales with the button's own font size */
.btn {
  font-size: 1rem;
  padding: 0.75em 1.5em; /* relative to this element's font-size */
}

.btn-lg {
  font-size: 1.25rem;
  /* padding automatically larger — no overrides needed */
}

.btn-sm {
  font-size: 0.875rem;
  /* padding automatically smaller — no overrides needed */
}

/* Line height — em keeps it proportional to font size */
p {
  font-size: 1rem;
  line-height: 1.6em; /* or just: line-height: 1.6 (unitless is better here) */
}

The compounding problem with em

The biggest mistake with em is using it for font sizes in nested components. Because em is relative to the parent, nesting multiplies the values and text grows or shrinks out of control.

/* The compounding problem */
.parent { font-size: 1.2em; } /* 19.2px (1.2 × 16px) */
.child  { font-size: 1.2em; } /* 23px (1.2 × 19.2px) */
.grandchild { font-size: 1.2em; } /* 27.6px — unintended */

/* Fix: use rem for font sizes — no compounding */
.parent     { font-size: 1.2rem; } /* 19.2px */
.child      { font-size: 1.2rem; } /* 19.2px — same, no compounding */
.grandchild { font-size: 1.2rem; } /* 19.2px — predictable */

Rule: Never use em for font-size on components that may be nested. Use rem for font sizes everywhere. Reserve em for padding and margin on self-contained components where you explicitly want the spacing to follow the component's own font size.

The decision framework

When you are about to write a CSS value and need to pick a unit, run through this in order:

  1. Is it a border, shadow, or media query breakpoint? → Use px
  2. Is it a font size? → Use rem, always
  3. Is it padding or margin on a component that has its own font-size? → Use em so spacing scales with the component
  4. Is it padding, margin, or gap in a layout context? → Use rem for consistency
  5. Is it an icon or image that must stay fixed? → Use px
/* A complete example applying the framework */

html { font-size: 16px; }       /* px — base only */

body { font-size: 1rem; }        /* rem — font size */

h1   { font-size: 2rem; }        /* rem — font size */

.card {
  padding: 1.5rem;              /* rem — layout spacing */
  border: 1px solid #2d3148;  /* px — border */
  border-radius: 8px;           /* px — visual decoration */
  box-shadow: 0 2px 6px rgba(0,0,0,0.2); /* px — shadow */
}

.btn {
  font-size: 0.875rem;          /* rem — font size */
  padding: 0.6em 1.2em;         /* em — scales with button's own font size */
  border: 1px solid;            /* px — border */
}

@media (max-width: 768px) {      /* px — breakpoint */
  h1 { font-size: 1.5rem; }      /* rem — font size */
}

What breaks when you pick the wrong unit

px for font sizes — accessibility failure

When a user sets their browser default font size to 20px for readability, px font sizes ignore that setting completely. Text stays at whatever pixel value you wrote. Users who depend on browser font scaling — often older users or those with visual impairments — cannot override your layout. This is a WCAG accessibility issue.

em for nested font sizes — compounding

Covered above. Each level of nesting multiplies the previous value. A component tree with three levels of 1.1em font-size results in text 33% larger than intended at the deepest level. Use rem to prevent this entirely.

rem for component padding — inflexibility

If you build a button with rem padding and then create a small variant by reducing the font size, the padding stays the same — the button looks too spacious for the smaller text. Using em for padding means the spacing automatically adjusts when you change the font size.

Frequently asked questions

Should I ever use px for font size?

Rarely. The only legitimate case is when you explicitly need text to be a fixed size regardless of user preferences — for example, legal disclaimers that must be exactly a specified print size, or UI elements where exact pixel control is legally or contractually required. For everything else, use rem.

Is rem always better than em?

No. They solve different problems. rem is better for font sizes and global spacing because it is predictable and does not compound. em is better for component-internal spacing — padding, margin, gap — because it scales automatically when you change the component's font size. Using only rem everywhere means you have to override spacing manually for every size variant of every component.

What about viewport units like vw and vh?

Viewport units are a fourth category — relative to the browser window size rather than font sizes. They are useful for full-screen layouts, fluid typography at the page level, and sizing elements relative to the viewport. They are a separate tool from px, em, and rem rather than a replacement for them.

Does Tailwind CSS use rem?

Yes. Tailwind's spacing and typography scale is built on rem by default, with a base of 1rem = 16px. Its spacing scale (p-4 = 1rem, p-8 = 2rem etc.) follows the same principles covered in this guide. If you are migrating from a utility-class framework to custom CSS or vice versa, the rem values are directly equivalent.

How do I convert px values to rem quickly?

Divide the pixel value by the root font size. With a 16px root: 24px ÷ 16 = 1.5rem. For bulk conversions or when working with a non-16px base, a dedicated converter is faster than doing the arithmetic manually each time.

Convert px to rem instantly

Paste any pixel value — or a list of values — and get the rem equivalent immediately with configurable base font size.

Open PX to REM Converter →