#react#performance

A Practical Guide to CSS Container Queries

· 3 min read

This post started as a note to my future self. Every few months I relearn the same lessons about container queries, so this time I'm writing them down.

The setup was ordinary: React at the edges, Storybook doing most of the heavy lifting, and a handful of scripts nobody wanted to own. It worked well until usage roughly doubled over a single quarter.

How we got here

We had good intentions. We even had a document describing how container queries should work. The trouble was that the document and the code had quietly gone their separate ways.

Most of the complexity was accidental. Over the years we'd layered three different approaches to container queries on top of each other, and each one made sense at the time.

What we changed

The fix turned out to be smaller than expected. Instead of adding another layer, we removed one and leaned on what React already gives you out of the box.

Here's a trimmed-down version of what we ended up with:

export function debounce<T extends (...args: any[]) => void>(fn: T, wait = 200) {
  let timer: ReturnType<typeof setTimeout> | undefined;
  return (...args: Parameters<T>) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), wait);
  };
}

This alone let us delete a surprising amount of code elsewhere in the project.

A few things that helped along the way:

  • Keep the old path around until the new one has survived a real incident.
  • Pair on the first few changes so the knowledge doesn't live in one head.
  • Write the migration guide before writing the migration.
  • Delete dead code aggressively; it's the cheapest refactor there is.

Trade-offs

We still haven't solved form validation. That's a post for another day, and honestly, I'm not sure we'll get it right the first time either.

None of this is free. The new approach is a bit more verbose, and newcomers occasionally ask why we don't just use TypeScript for everything. That's a fair question with a boring answer: we tried, and it made container queries harder to reason about.

What's next

Six months in, nobody on the team wants to go back. That's about the best review a change like this can get.

HR
Hazel Rossi
Frontend engineer in San Diego. Writing about frontend engineering.