Just Use HTML

2025-09-1612:4512797gomakethings.com

I’ve worked on so many projects recently that were more complicated than they needed to be because they used JavaScript to generate HTML. JavaScript is… Slower to load Slower to run More prone to…

I’ve worked on so many projects recently that were more complicated than they needed to be because they used JavaScript to generate HTML.

JavaScript is…

  • Slower to load
  • Slower to run
  • More prone to breaking
  • Harder to read and reason about
  • Doesn’t actually look like the final output

It’s inferior to just using HTML in nearly every way.

I’m not saying never use JavaScript, though. I think JS is great at augmenting and enhancing what’s already there, and adding interactivity that cannot (yet) but handled with HTML.

Let’s look at two examples…

I see this a lot in React and JSX.

Every input in a form has an input listener on it. Any changes to that input update a state property. That property is used to set the value of the input, creating this weird circular logic.

(This approach is called “controlled inputs” in React-land, and some devs are slowly moving away from it, finally.)

The form submit is often also tied to clicking a <button> rather than submitting a form, meaning that hitting enter on an input won’t submit the form. This removes a native accessibility feature.

function Login () { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); function handleSubmit () { if (!username || !password) { // Show error message
 return; } fetch('/login', { method: 'POST', body: JSON.stringify({ username, password }), }); } return ( <form onSubmit={event => event.preventDefault()}> <label for="username">Username</label> <input id="username" type="text" onInput={event => setUsername(event.value)} value={username} /> <label for="password">Password</label> <input id="password" type="password" onInput={event => setPassword(event.value)} value={password} /> <button onClick={handleSubmit}>Submit</button> </form> );
}

Here’s that same setup with HTML…

<form action="/login" method="POST"> <label for="username">Username</label> <input id="username" type="text" required /> <label for="password">Password</label> <input id="password" type="password" required /> <button>Submit</button>
</form>

And then you can enhance it with just a touch of JavaScript…

const form = document.querySelector('[action*="/login"]');
form.addEventListener('submit', event => { event.preventDefault(); const data = new FormData(form); const body = JSON.stringify(Object.fromEntries(data)); fetch('/login', { method: 'POST', body });
});

Hell, you can even do this in React if you want!

function Login () { function handleSubmit (event) { event.preventDefault(); const data = new FormData(event.target); const body = JSON.stringify(Object.fromEntries(data)); fetch('/login', { method: 'POST', body }); } return ( <form onSubmit={handleSubmit}> <label for="username">Username</label> <input id="username" type="text" required /> <label for="password">Password</label> <input id="password" type="password" required /> <button>Submit</button> </form> );
}

API responses

Another area where you can lean a lot more heavily on HTML is API responses.

Let’s say you have a <table> that gets generated based on some data that’s specific to the user or some selections that have been made in a <form>.

In most modern apps, that means getting back some JSON, and generating a <table> from it.

const app = document.querySelector('#app'); const request = await fetch('/my-wizards');
const response = await request.json(); app.innerHTML = `<table>
 <thead>
 <tr>
 <th>Name</th>
 <th>Location</th>
 <th>Powers</th>
 </tr>
 </thead>
 <tbody>
 ${response.wizards.map(wizard => { const {name, location, powers} = wizard;
 const row =  `<tr> <td>${name}</td> <td>${location}</td> <td>${powers}</td> </tr>`; return row; }).join('')} </tbody> </table>`;

But if a server has to do the work of getting that information and sending it back to you anyways, it could also just send the <table> HTML, which you could then render into the UI.

const app = document.querySelector('#app'); const request = await fetch('/my-wizards');
const response = await request.text(); app.innerHTML = response;

There are workflow changes

This, of course, changes the workflow of building apps quite a bit.

A lot of work shifts to the backend that in today’s apps is handled with client-side code. But… that’s a good thing?

It means faster, simpler apps that behave more predictably and reliably.


Read the original article

Comments

  • By mmcromp 2025-09-1613:473 reply

    This isn't scalable for any kind environment with multiple services and teams. Can you imagine "actually the table display will be handled by the User service BE, we'll just inject it". The reason why people reach for react and js for simple projects is because that's what theyre familiar with at work (that or they're training in hopes of work), even when theoretically they could of used something way more stripped down

    • By RUnconcerned 2025-09-1614:153 reply

      There's nothing stopping an HTTP API from returning both HTML and JSON from the same endpoint. Just have the client send "text/html" or "application/json" in the Accept header, depending on what it needs.

      • By zffr 2025-09-1614:532 reply

        One challenge is that JSON is for data and HTML is for UI. When a client gets JSON it can transform the data to make it ready for the UI. With HTML, the client is locked into the UI chosen by the server.

        What if the client wants to render the output of an API in different ways depending on what screen is visible? If the server API outputs JSON, this is trivial. If it outputs HTML the client is stuck into rendering what the server gives it.

        • By Izkata 2025-09-1615:06

          That's why GP mentioned the Accept header, the client can choose at runtime which one it wants.

        • By lucyjojo 2025-09-1710:091 reply

          i don't do frontend so apologies if it's a dumb question.

          if html is for ui, what is css for?

          i always thought html was for documents/data (like xml light)

          • By Bolwin 2025-09-181:001 reply

            Html is for structure, css for appearance. OP's point still applies

      • By cenamus 2025-09-1614:22

        However it would make sense to have a separate json api for other applications. That way the html endpoints can change without without api versioning, perfectly matching whatever the gui needs.

      • By jonkoops 2025-09-1614:31

        There absolutely is, this is just extra cruft you need to maintain, and who says that the HTML is universal enough to be used everywhere? This is exactly where a front-end or a backend-for-frontend (BFF) makes sense.

    • By zdragnar 2025-09-1614:513 reply

      It's not scalable to a small team either. The amount of copy/paste of markup I had to clean up on a site that didn't actually contribute to style, presentation or information was insane. Just divs and divs of tailwind classes all overriding each other or having no effect copy pasted everywhere.

      Far better to have a tool that lets you define small, reusable, composable building blocks and let your team just use those.

      • By debo_ 2025-09-1615:42

        Web components work ok, and so does templated html. I was making reusable html components with Django templates in like, 2008.

      • By em-bee 2025-09-1615:091 reply

        exactly, even for a single person maintaining just a few pages with a bit of style and navigation is already a chore. i'd rather use a js framework or even xslt than edit the menu on every page, every time i need to add a new page.

      • By librasteve 2025-09-1615:28

        just to mention you CAN do this with HTMX using a server side library… there are many, personally I like https://harcstack.org (because i wrote it)

    • By loloquwowndueo 2025-09-1613:531 reply

      * could have

  • By renegat0x0 2025-09-1613:024 reply

    I performed some transition from django pure template solution to more javascript oriented.

    - my server was running on raspberry PI, now heavy responses are returned via JSON, I think REST-like data. Which makes me offload burden to clients

    - javascript makes the page more responsive. Parts can be loaded via separate calls, and it divides something that would be otherwise a monolith into subsequent small calls

    - In my experience nearly all simple binary decisions "use only X", and "use only Y" solutions are bad

    • By gwd 2025-09-1613:24

      > Parts can be loaded via separate calls, and it divides something that would be otherwise a monolith into subsequent small calls

      He's not saying don't use Javascript; he's saying that instead of having those small calls shipping JSON which is then converted into HTML, just have those small calls ship HTML to begin with.

      It's surprising how far you can get with something like HTMX.

    • By endemic 2025-09-1615:32

      > Parts can be loaded via separate calls

      Just makes me think of my bank's site, which shows a few seconds of "loading" animations on each section of the page. Maybe if the main content loaded quickly and ancillary content was lazy-loaded it would feel faster.

    • By naasking 2025-09-1614:23

      > it divides something that would be otherwise a monolith into subsequent small calls

      This is often at odds with page responsiveness, and can increase server load considerably.

  • By niux 2025-09-1613:196 reply

    This article is an oversimplification describing basic forms, but as soon as you try to implement any sort of advanced validation using just HTML, the whole paradigm falls apart. Browsers support JavaScript for a reason.

    • By dreadnip 2025-09-1613:337 reply

      How can you ever trust validation on the client side though? The user can just mess with it and submit anything they want anyway.

      Why not validate on the server and return a response or error as HTML?

      I’m not trying to argue in bad faith here. I know client side validation is necessary in some cases, but IMO it’s only an augmentation for UX purposes on top of server side validation.

      • By Tade0 2025-09-1613:39

        > Why not validate on the server and return a response or error as HTML?

        If your form has files you'd want to at least check it before sending data unnecessarily to the server.

        Also it's always better to have a tighter feedback loop. That is really the main reason why there's validation on the frontend and backend, not just the latter.

      • By palmfacehn 2025-09-1614:13

        Typically for fetch() I return the error as "text/plain" with the appropriate status code. It is still necessary to give a preflight validation in Vanilla JS, apply an error style and helpful feedback.

        HTML has come a long way since the bad old days. General constraints can be applied directly to the input element. It still makes sense add additional explanations for the invalid input with JS though.

        https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/...

      • By prokopton 2025-09-1615:20

        Client-side validation is a progressive enhancement. You always have server-side validation. The validation in the browser exists to provide additional feedback that lets users have realtime feedback about their input.

      • By carlosjobim 2025-09-1615:14

        You have a human in the loop somewhere. This is how we do to not lose clients who accidentally typed @gmail.co

        It's also essential in order to not make forms to difficult to fill in, because that means you lose customers and a lot of money.

      • By jonkoops 2025-09-1614:33

        Just use a generic data structure for your validation rules that you can apply on the front-end, and validate on the back-end. Using JavaScript and doing validation on a server are not mutually exclusive.

      • By e12e 2025-09-1614:23

        You're answering yourself here: client side feedback/validation is essential ux for any complex form. Even for simple signup forms with a few required fields.

      • By lezojeda 2025-09-1614:35

        [dead]

    • By graemep 2025-09-1613:401 reply

      You can use JS to provide validation and still submit the form from HTML.

      • By skydhash 2025-09-1614:174 reply

        That’s not validation. It’s more like helpful hints.

        • By naasking 2025-09-1614:26

          Helpful hints are all you can rely on from client-side. Client-side validation doesn't really exist, true validation only happens server-side.

        • By fkyoureadthedoc 2025-09-1614:201 reply

          A pointless distinction when we all know what we're talking about.

          • By skydhash 2025-09-1614:231 reply

            Is it? I’ve seen devs bring complicated code to the frontend when a required attribute on the tag would have done the job just as well. Preventing user mistakes is not the same as ensuring correct input.

            • By fkyoureadthedoc 2025-09-1614:30

              We've all seen bad implementations of any given thing I'd imagine. Ever submit a form that triggers a full page refresh after about 8 seconds of hanging, only to lose half the data and reveal some arcane requirement to you that wasn't stated up front? Of course this isn't the proper way to do it, but it happens.

        • By graemep 2025-09-1614:39

          What is the difference?

          The same is true for any JS "validation" and I was using common terminology.

          From a user point of view as long as you keep the feedback loop short what difference can they see?

        • By cenamus 2025-09-1614:24

          Neither is it validation when you use JS if the server doesn't check it. You can always send requests with postman, curl or your tool of choice.

    • By norman784 2025-09-1613:45

      You can have a minimal library like HTMX or if you have access to a solution Phoenix LiveView, you need minimal JS to have an interactive page, the only downside is that you could introduce some latency because of the network trip.

    • By leftyspook 2025-09-1613:33

      I think a part of the point is to question the need for things like that. Do your users actually need all of these bells and whistles?

    • By port11 2025-09-1619:48

      There's a lot of validation that can be done with HTML; I'd even say most client-side validation can be handled by HTML/the browser. You can specify input types, do RegExp matching, etc. There's a lot in the spec.

      You'll need server-side validation anyway…

HackerNews