# Removing React.js from the codebase and adapting HTMX for UI interactivity

Here's how Misago works currently:

1. You request `https://misago-project.org`.
2. Django view gathers data to render list of threads.
3. Django view renders template that contains almost complete HTML with list of threads, but also includes a JSON with same data used to render this HTML. And there is no interactivity because forms are disabled.
4. JavaScript downloads.
5. JavaScript runs, reads JSON embedded in the page's body and replaces most of HTML rendered from Django templates with HTML from [React.js](http://React.js) components. Buttons become active, and timestamps in UI swap.

This approach has some problems:

- A lot of pages in Misago are implemented twice: as Django templates and [React.js](http://React.js) components.
- People who start customizing HTML get burned because they edit Django templates and see their changes flash for a second on a site before being replaced by [React.js](http://React.js) HTML which they didn't know they also need to customize.
- Every view accessible to both users and guests needs to be done twice: as Django view with templates, and as [React.js](http://React.js) route with components. It also requires an API and JSON serializers to power data fetching.
- JSON serialization to pre-bake data for [React.js](http://React.js) app slows down response generation.
- A large part of translation messages is duplicated, living in both `django.po` and `djangojs.po` files. JavaScript translation files also increase the initial download size.
- Lots of JavaScript can kill performance, especially on older and slower mobile devices.
- Enabling plugins to replace or inject custom HTML to the page requires for those plugins to implement both Django templates and [React.js](http://React.js) components. And Misago will need to implement a JavaScript build step as part of site build in `misago-docker`. Plugin devs need to know both.

I've considered two solutions to this problem:

1. Drop Django views and templates, only keep those in minimal versions to keep search engine bots happy. Focus on implementing an API and having all UI as [React.js](http://React.js) app.
2. Reduce Django to API and use JavaScript framework with serverside rendering, eg. [Next.js](http://Next.js) or [Remix.run.](http://Remix.run.)

But here's a thing: there's a lot of forum software out there that still does the old way of rendering as much as possible on the server and using some JavaScript on client to improve its interactivity here and there. And people are **happy with that**. And this approach has *none* of the above issues.

Internet forum software has plenty of interactivity, but this interactivity is isolated to specific places on the page. Moderation actions, watching a thread, writing a reply, seeing last notifications, voting in a poll. All this stuff can be achieved without [React.js](http://React.js) and was achieved without it for years before we've decided that full page reload is something that needs to be avoided.

Now, what's HTMX? [HTMX](https://htmx.org/) is a tiny library that lets developers specify parts of HTML as dynamic islands that can be swapped by new server-rendered HTML on interaction. For example, list of actual threads is one such (large) island. With little bit of HTMX included in Django template, changing current category on threads list could pull new HTML from Django only for new threads list for selected category, while keeping rest of the page's HTMX (eg. already loaded search results in navbar) unchanged. Misago's backend code would only need to be changed to return only the HTML for this island when request comes from HTMX, instead of full page. There's no need to do any JSON serialization or write dedicated JavaScript or [React.js.](http://React.js.)

HTMX is declarative way of doing `$.get("url", "#outlet")` we've did in jQuery 20 years ago. Or Rails Turbolinks. I can't believe I am writing this, but this is the way for forum software if you hate endless scrolling or want to keep things simple otherwise.

I've also decided against moving the admin panel to SPA. Currently I have a bunch of reusable Django views that take care of 90% of work, and adding new page to admin requires one to pick right base view, fill in blanks, define one or few forms and write very basic templates for those. Rest happens automagically and I like it.

The effort required to move away from [React.js](http://React.js) and to HTMX will be a big one, but it can be done as multiple small steps. Eg. one release could move Navbar to HTMX, other threads list, next thread page, another user profiles, etc. etc. But plugins/permissions/new parser still have to happen first. So HTMX migration will have to wait until later 2024. I am fine with that becasue I would also want to move to latest Bootstrap, and those guys are cooking some crazy stuff in it.

For the record, JS sizes from Misago 0.39:

`vendor.js`: 679kb, 214kb gzipped  
`misago.js`: 615kb, 124kb gzipped  
`django-i18n.js`: 102kb, 25.4kb gzipped

Misago JS also includes lazily loaded:

`hljs.js`: 144kb, 49kb gzipped  
`zxcvbn.js`: 820kb, 430kb gzipped

I plan to return to those numbers once I start removing [React.js](http://React.js) (and other JS dependencies) from Misago.

I also need to give a warning that we will likely have a transition period where parts of Misago will have no [React.js,](http://React.js,) but also no HTMX either, effectively making them "multi-page application". This may sound scary but it only means that clicking a link or submitting a form in, say, user options page will cause a full page reload. But I will still write a placeholder AJAX (or just use the HTMX) where full page reload will be absolutely unbearable, eg. for liking posts or voting in polls.

["Forum options" page has been replaced with "Account settings"](https://github.com/rafalp/Misago/pull/1742). This new page is done fully with Django views, and uses HTMX on some pages for dynamic updates.

JS size change:

`misago.js`: 578kb, 107kb gzipped (37kb/17kb less)

[Threads lists](https://github.com/rafalp/Misago/pull/1758) have been rewritten almost from scratch to use Django views and HTMX instead of [React.js.](http://React.js.)

JS size change:

`misago.js`: 530kb, 99kb gzipped (48kb/8kb less)