Back
7 min read

Why I reach for vanilla first

  • HTML
  • CSS
  • JavaScript
  • Philosophy

When I start a new project, I reach for vanilla first. I try to solve the problem with native HTML, CSS, and JavaScript before I consider adding a framework or library.

The framework reflex is understandable. A new project starts, the requirements land, and before you have finished the brief, that framework is already on the dependency list. That is not laziness. It is pattern matching. These tools solved problems before, so we assume they will solve this one too.

The native baseline is stronger

This argument was weaker in the late 2000s and early 2010s. In that period, jQuery often felt essential. The DOM API was rough, browser inconsistencies were common, and the platform had obvious gaps. Those libraries were practical answers to real constraints.

Today, the baseline is different. Modern browsers now ship CSS Grid, custom properties, container queries, popover, inert, color-mix(), subgrid, scrollbar-gutter, content-visibility, dialog, :has(), and @layer. The gap between platform and library capabilities has shrunk drastically, and for many UI tasks it has effectively closed.

The cost of dependencies

A dependency always carries trade-offs. You inherit maintenance, upgrade pressure, bundle weight, and another abstraction between you and the thing you are building.

Sometimes that trade-off is worth it. A robust charting library can save weeks of work. A date library can still be useful where you need legacy support, helper APIs, or codebases that already rely on one. If Temporal is available and fits the job, that should be the first place you look.

But modal dialogs? The <dialog> element ships in every modern browser, handles focus management, responds to the Escape key, provides a ::backdrop pseudo-element, and exposes showModal() and close() as a native API. A library that replaces it doesn't add capability; it adds indirection.

The same applies to styling systems. If a project only needs a modest design system, custom properties plus @layer and a small set of well-named classes can give you clarity and longevity with less build overhead.

Why native code holds up

CSS written in 2010 still works. HTML written in 1995 still renders. JavaScript written to the ES5 standard still runs in every browser on earth.

React 15 does not run in 2025 without upgrades. Angular 1 applications are effectively stranded. That's not a criticism; frameworks evolve, and evolution is healthy. But it does mean that the half-life of framework-dependent code is structurally shorter than the half-life of platform-dependent code.

For a long-lived project, that matters. For a personal site that you'd rather not touch every eighteen months to keep the dependency tree green, it matters even more.

What you learn by building it yourself

There is another benefit to vanilla work: you understand what you built.

When a framework handles something for you (accessibility, animation, state), you can use the outcome without understanding the mechanism. That's fine until the day the outcome is wrong and you have no model for why. Debugging an abstraction you don't understand is a specific kind of miserable.

Building on the platform forces you to understand the platform. <dialog> and focus trapping teach you how focus management works. Writing a scroll animation with animation-timeline: scroll() exemplifies how the browser thinks about scroll position. That understanding compounds over time, and when a bug does show up you have a much better chance of finding it quickly.

When to reach for a framework

This is not a rejection of frameworks. It is a reminder that they should answer a problem, not start the conversation.

Reach for a framework when the platform genuinely can't do what you need: complex state management across a large application, Server Side Rendering (SSR) with hydration, a mature component system, and strong team familiarity are all valid reasons. If the trade-off is worth it, make it deliberately.

Start with the platform. Make dependencies earn their place through clear capability, team impact, and maintenance cost, not familiarity.