# Building textlog without JavaScript

> Source: <https://gist.github.com/stagas/09ad937b493bf8cd3285917279de2488>
> Published: 2026-08-26 20:02:39+00:00

# Building textlog without JavaScript

> ai;dr — This post has been enhanced with AI.

When I started building [textlog](https://textlog.cc), I gave myself a constraint: no JavaScript in the browser.

Not “minimal JavaScript”, or “JavaScript only where necessary” which inevitably turns into a bundle, a router and hydration a few weeks later. I wanted the normal experience of using the site to be plain HTML and CSS.

The slightly funny thing is that textlog is still a React app. React just never reaches the browser.

A request comes in, we get whatever data we need, React renders the page on the server and we send the resulting HTML. There is no hydration afterwards and no second application waking up in the browser. The HTML arrives and that's the page.

At first this mostly felt like a technical experiment. I wanted to see how much of a modern web app I could actually build this way. But after working on it for a while, the constraint started influencing almost every decision I made about textlog, including decisions that had very little to do with JavaScript.

The first thing I noticed was how much HTML already does.

When you've been building client-side applications for years, your instinct when you need an interaction is to write some state and an event handler. Without that option, you have to look again at what the browser can already do.

Links navigate. Forms change things. URLs hold state. Radio buttons are pretty good at being radio buttons. `<details>` opens and closes. Inputs validate themselves. CSS can respond to more state than I remembered.

A poll, for example, doesn't really need JavaScript. It's a form with some radio buttons. You vote, the server receives the vote, and the next page contains the results.

Filtering a feed doesn't need client state either. The filter can just be part of the URL. Pagination is links. Following someone is a form submission. Editing a post is another form.

There were many moments where my first thought was “this is going to be difficult without JavaScript”, followed a few minutes later by realizing that the boring HTML solution was completely fine.

And boring became a feature.

The harder part was changing how I thought about interactions.

If you approach the problem as “how can I recreate this SPA interaction without JavaScript?” you usually end up fighting the browser. The more useful question is “what would the web version of this interaction look like?”

Something doesn't necessarily have to update instantly in place. You can submit a form, redirect, and render the new state. A filter doesn't have to live invisibly inside a component. It can live in the URL. A piece of UI doesn't always need to appear in a modal. Sometimes it can be a page.

This sounds almost embarrassingly obvious when written down. It's basically how websites worked before we decided every website should behave like a desktop application.

There are also some nice consequences.

URLs become meaningful again. You can copy them. You can bookmark them. You can open things in another tab. Back and forward work because we're not trying to replace them with our own idea of navigation. Refreshing the page isn't a dangerous operation that might destroy some mysterious pile of client state.

The server side also becomes pleasantly straightforward. The server already knows who you are, what you can see, what you've read and what the database contains. It doesn't have to expose all of that through an API so another application running in the browser can reconstruct the same state.

It gets the data, renders the page and sends it.

When something changes, the server changes it and renders the new page.

There are fewer moving pieces and, maybe more importantly, fewer opportunities for those pieces to disagree with each other.

Performance also became less of a problem simply because there is less happening. There isn't an application bundle to download and parse, there is nothing to hydrate, and there isn't a framework runtime waiting around after the page has loaded. Textlog is mostly text, so the pages themselves are tiny. The browser gets HTML and starts rendering it.

Of course, server round trips still exist. I'm not claiming this is somehow faster than a local client interaction. But I like the trade. Instead of building a complicated client and then spending time making it feel fast, we removed most of the client.

There are limits, and I've run into them.

Some browser features really do require JavaScript. Push notifications are one example. You need JavaScript to register a service worker and ask the browser for permission.

For those cases I decided not to turn the constraint into a religion. Textlog has a dedicated place where JavaScript can exist when a browser API genuinely requires it. Enabling notifications can happen on a page that loads JavaScript. That doesn't mean the timeline suddenly needs JavaScript too.

That distinction has become more important to me than “zero JavaScript” as a slogan.

JavaScript is allowed. It just has to have a reason to be there.

Something else happened that I didn't expect: the technical constraint started shaping the personality of textlog.

Textlog is supposed to be a quiet place. It's text only. There are no likes or reactions. I'm deliberately trying not to build another interface that constantly asks for your attention.

When adding an interaction isn't completely free, you start questioning interactions more.

Do we need this popup? Does this number need to update live? Does this menu need to float over the page? Does this action need an animation? Does the user need to be notified about this immediately?

Quite often the answer is no.

So the lack of JavaScript stopped feeling like something I had to work around and started feeling like part of the design.

I definitely wouldn't build everything this way. There are applications where a rich client is obviously useful and sometimes essential. But building textlog has made me think we've become a little too quick to put an application runtime in the browser.

A lot of what we build is still people reading things, following links, filling in forms and changing some state on a server.

HTML happens to be extremely good at that.

Textlog is React on the server, HTML in the browser, forms when something needs to change, URLs when something needs to be remembered, and a little JavaScript in the few places where the browser actually demands it.

None of this is particularly new.

That's probably my favorite thing about it.

Curious to see how it looks like? [Head over to textlog](https://textlog.cc) and see for yourself!


