Frameworks earn their place when scale demands them, but plenty of products sit in a band where structured vanilla JavaScript keeps dependency graphs legible and onboarding cheap for both developers and operations.

The common failure mode is treating “vanilla” as one giant file. The fix is borrowing component thinking without committing to a particular runtime.

What I mean by component here

A component is not synonymous with JSX — it is a self-contained slice of client behaviour with accessible markup, intentional styling scope, and predictable lifecycle hooks. Expose a small public surface: mount, destroy, or plain DOM events.

The rule of thumb: no module reaches into someone else’s DOM tree. Prefer Custom Events or a tiny in-memory bus keyed by topic names instead of everyone holding direct references to everyone.

Folder layout that stays calm as you grow

Patterns that work well for content-heavy sites or midweight dashboards:

  • components/ — one folder per block (hero/, filter-bar/, …) with Component.ts, styles.css, and optional templates or helpers.
  • lib/ — shared utilities that never import UI primitives.
  • pages/ — composition only: glue that imports and mounts components.

Whether styles import from modules or bundle through Astro/Vite matters less than co-location with the behaviour they describe.

Perceived performance and bundle shapes

A direct win: you ship JS for the islands you actually hydrate. Compared to a framework tree that boots large subtrees even when off-screen, disciplined vanilla keeps cost proportional to rendered surface.

That matters when Lighthouse scores or slow networks are part of the conversion story — tying back to other posts on Core Web Vitals work.

Growing without a rewrite cliff

Start from semantic static markup, then add layers:

  1. Keep state local to each component until a real need for shared stores appears.
  2. Add a minimal router that toggles sections or maps URLs without pretending to be Next.js on day one.
  3. Later, if reuse explodes, compile toward Web Components or adopt a framework only where the UX budget justifies it.

Conclusion

Component architecture does not require JSX or a virtual DOM — it requires clear contracts, boundaries, and habits indistinguishable from a mature front-end team, with the upside of shipping less abstraction until the product proves it needs more.


Back to Archive