DEV Community

Cover image for I’m SO Looking Forward to Not Needing a Build System Again
Raheel Shan
Raheel Shan

Posted on • Edited on • Originally published at raheelshan.com

I’m SO Looking Forward to Not Needing a Build System Again

Have you been working on the frontend with Javascript? Have you ever noticed what used to be simple <script> has become a full build system? Frontend development has involved more tools than needed just to feel "modern," "reactive," and "interactive". Lets first talk about the problems.

The Compromised SEO

This is the most important issue. When users or crawlers come to visit our page, the page is empty with a div with an id. Users wait for the page to be loaded, but crawlers go back, marking this page empty without data. This heavily affects the ranking in search engines. To counter this issue, people started to use static site generation. Now that the pages are statically generated, they are not updated frequently, yet another problem to be solved.

The Markup Drift

The real problem isn’t JSX syntax—it’s that markup moved to the JavaScript end. When that happened, the backend stopped sending HTML and started sending JSON instead.

Now the frontend has to do many things.

1. The Build Tool Spiral

Once your markup lives in JavaScript, raw files can’t run directly in the browser anymore. You need a build pipeline—Babel, TypeScript, Vite, Webpack, or something else—to transpile JSX, resolve imports, bundle dependencies, minify assets, and compile everything into a format the browser understands. What started as a simple template now requires a full compiler chain just to draw a div.

2. The TypeScript Dependency

Because JSX is code, not markup, you end up mixing presentation with typing rules, interfaces, and generics. You can’t just “return HTML”; you have to satisfy the TypeScript compiler before you can even see what your component looks like.

3. The Tooling Cascade

Once you add TypeScript, you add linters. Once you add linters, you need formatters. Once you add formatters, you add CI checks to enforce them. Your “simple view component” now depends on Node, npm, ESLint, Prettier, and a small stack of plugins. Isn't that great? :)

4. The Build-Time Wall

Every change to the frontend must now pass through a build. You can’t tweak markup and refresh—you have to wait for the bundler to recompile, rebuild, and reload.

5. The Fragile Runtime

If JavaScript fails, the UI fails. Since the markup is built at runtime, there’s no fallback HTML. One syntax error, one missing dependency, or one hydration mismatch and the entire interface collapses—blank screen, cryptic console errors, broken user experience.

6. The Over-Delegation Problem

We’ve given JavaScript far more responsibility than it deserves. It’s now responsible for:

Fetching data
Building markup
Managing state
Rendering updates
Coordinating routing
Each of these used to be handled by the web platform or the backend. Now they all sit on the same fragile runtime that wasn’t designed for this workload.

7. The JSON Middleman

Because markup is gone from the backend, every UI element now depends on a JSON contract. Controllers return data, not views; the frontend reinterprets it into markup. So two systems—backend and frontend—must now stay perfectly in sync about what “a post” or “a user” looks like. That’s duplication. That’s maintenance debt.

It's time to pause and see why we chose a path we never had to in the first place. 

What Backend Can Do?

The backend has always been capable of full interactivity. It can render and persist state, handle routes, validate input, and return partial HTML—everything a frontend framework does, but faster and more predictably.

Any backend framework doesn't need a frontend babysitter. It already knows how to build reactive, server-driven interfaces—we just forgot to let it.

How Backend Used to Work and Can Still Do

The backend always gave reactivity on two simple tags.

  •  (anchor) tags for navigation
  •  elements for interaction

Take a look at the snippet below.

<a href="/blog">Blog</a>
Enter fullscreen mode Exit fullscreen mode

This anchor tag tells a browser:

When a user clicks on this link, issue an HTTP GET request to ‘/blog’ and load the response content into the browser window

Here's with the <form> tag.

<form method="post" action="./save-post" >
    <button type="submit" >Save</button>
</form>
Enter fullscreen mode Exit fullscreen mode

The form tag tells the browser:

When form is submitted, issue a post request, process data in the server and let server issue an HTTP Get request in response to form and load the response content into the browser window.

HTMX: HTML That Fights Back

Then comes HTMX, the library that makes HTML behave like it remembers how the web works. No build step. No bundler. No JavaScript build tools. Just attributes that turn your markup alive.

Now consider the following HTML snippet.

<button hx-post="/clicked"
    hx-trigger="click"
    hx-target="#parent-div"
    hx-swap="outerHTML">
    Click Me!
</button>
Enter fullscreen mode Exit fullscreen mode

This tells HTMX:

When a user clicks on this button, issue an HTTP POST request to ‘/clicked’ and use the content from the response to replace the element with the id parent-div in the DOM.  

HTMX has gone a step further and implemented this idea to other HTML elements so that they can be interactive as well.

And here's the summary taken from HTMX docs.

  • Now any element, not just anchors and forms, can issue an HTTP request
  • Now any event, not just clicks or form submissions, can trigger requests
  • Now any HTTP verb, not just GET and POST, can be used
  • Now any element, not just the entire window, can be the target for update by the request

And guess what? What are we receiving from the backend in response?

HTML, not JSON.

Receiving a response in HTML format is deliberate. This gives rendering/hydration responsibility back to the backend, which Javascript snatched in the name of interactivity.

What About Reactivity?

But yes—you’ll sometimes want UI state that reacts instantly without hitting the server. HTMX handles interactivity, not reactivity.

That’s where most people add Alpine.js. It’s light and works fine. I wanted to go one step further, though—something that truly treats the DOM as the source of truth. So I built JSRibbon.

JSRibbon: Reactivity From the DOM Outward

JSRibbon is my ongoing experiment in DOM-first reactivity—a small, component-based library that learns from the last decade of frontend chaos without copying it.

Here’s the heart of it:

The DOM is The State: No virtual layers or hydration. The HTML itself defines current data.

The DOM is the Markup: No additional markup on the Javascript end. DOM is the actual markup to render more nodes when needed.

Event Delegation is Core: Inspired by jQuery’s .on(), events bubble to component roots, so handlers never die when the DOM changes.

Component Composition and Context: Each component has its own scope but can pass state to children—React’s good ideas, minus the ceremony.

Mutation Awareness: A built-in MutationObserver means that if new HTML arrives from HTMX, JSRibbon automatically detects and wires it. Nothing to reinitialize, nothing to refresh manually.

The Quiet Revolution

The mix of Backend + HTMX + JSRibbon doesn’t look flashy—and that’s the point. It gives you reactivity, partial page updates, and component logic without a single build step. You deploy PHP, Blade, and two small JS files. That’s it.

No npm. No transpiler. No “dev mode.” Just clean, reactive, server-first HTML.

Why It Matters

The industry has over-engineered frontends into exhaustion. We ship megabytes of JavaScript to re-render what the server already produced perfectly. HTMX and JSRibbon offer an alternative path: HTML as the API, the DOM as the framework, and the server as the brain.

If this approach catches on—and it should—we may finally get to build interactive web apps that age gracefully instead of breaking every six months.

Top comments (0)