Readit News logoReadit News
Posted by u/amaury_bouchard 5 days ago
Show HN: µJS, a 5KB alternative to Htmx and Turbo with zero dependenciesmujs.org...
I built µJS because I wanted AJAX navigation without the verbosity of HTMX or the overhead of Turbo.

It intercepts links and form submissions, fetches pages via AJAX, and swaps fragments of the DOM. Single <script> tag, one call to `mu.init()`. No build step, no dependencies.

Key features: patch mode (update multiple fragments in one request), SSE support, DOM morphing via idiomorph, View Transitions, prefetch on hover, polling, and full HTTP verb support on any element.

At ~5KB gzipped, it's smaller than HTMX (16KB) and Turbo (25KB), and works with any backend: PHP, Python, Go, Ruby, whatever.

Playground: https://mujs.org/playground

Comparison with HTMX and Turbo: https://mujs.org/comparison

About the project creation, why and when: https://mujs.org/about

GitHub: https://github.com/Digicreon/muJS

Happy to discuss the project.

recursivedoubts · 5 days ago
heya amaury, great library!

i have added it to the htmx alternatives page:

https://htmx.org/essays/alternatives/#ujs

tagfowufe · 5 days ago
Sneaking in real quick to thank you for your contributions and positive attitude you bring to the space.

Dead Comment

amaury_bouchard · 5 days ago
Wow thanks a lot! That's very kind, I really appreciate it :)
gavmor · 5 days ago
I really like these sorts of frameworks from an architectural perspective, but what's the use-case? Maybe I'm too SPA-pilled, because to me all the fun of Web development is in providing really fluid, skeuomorphic experiences like those enabled by, eg pragmatic-drag-and-drop[0] or yjs[1].

I just struggle to envision what application benefits from the efficiency that this or htmx offer, but from neither the ultra-interactive, nor the ultra-collaborative. Maybe updating stock ticker prices? Fast-service back-of-house ticketing displays?

I would love to feel called to reach for this library.

0. https://github.com/atlassian/pragmatic-drag-and-drop

1. https://github.com/yjs/yjs

xhcuvuvyc · 4 days ago
Aside from being able to build fast websites: https://youtu.be/fWfIf7Vfjec

Relying on server side as the source of truth inverts complexity. Suddenly 90% of the modern webs problems evaporate. Turns out building everything on top of a shadow dom in the client creates a boatload of unforced errors.

amaury_bouchard · 4 days ago
The sweet spot is everything between a static website and a full SPA, which is actually most of the web. Think e-commerce (filtering, cart updates, pagination), dashboards (refreshing metrics, notifications), admin interfaces (inline editing, form submissions), content-heavy sites (live search, comments, modals). Basically any app where the primary interaction is 'user does something, server responds with updated content'. For drag-and-drop kanban boards or collaborative editors, you're right, µJS is not the right tool. But those are a small fraction of what gets built every day. Most web apps are glorified CRUD interfaces, and for those, server-rendered HTML fragments are simpler, faster to develop, and easier to maintain than a full SPA.
qingcharles · 5 days ago
I love that drag/drop, BTW. Really nice. Gonna see how I can use that.
nattaylor · 5 days ago
Reminds me a little of htmz

htmz is a minimalist HTML microframework for creating interactive and modular web user interfaces with the familiar simplicity of plain HTML.

amaury_bouchard · 5 days ago
Yes, htmz is a great project! The core idea is similar, but htmz is intentionally more minimal (it's literally 166 bytes). µJS covers more ground: patch mode, SSE, DOM morphing, View Transitions, prefetch, polling, full HTTP verb support.
ok_dad · 5 days ago
Yay someone already mentioned my favorite “framework”!

htmz is a masterclass in simplicity. It’s gotta be the all time code golf winner.

oso2k · 5 days ago
This looks interesting. Thanks for sharing.
whiterock · 5 days ago
quite cool, but the browser history being messed up drives me bonkers. is that easy to fix?
lastdong · 5 days ago
You should be able to push to history using history.pushState(…) after loading a new page. There are probably more details needed around popState to handle back and forward navigation. I’m sure Claude will be able to assist here, but maybe too much complexity when other similar libraries may also manage history state.
oso2k · 5 days ago
There’s several other (well) known examples of the use of mujs.

There’s Artifex’s interpreter from muPDF. It’s also the basis of several JS related projects: https://mujs.com/

There’s also a lesser known interpreter: https://github.com/ccxvii/mujs

And IIRC, there was a CommonJS library of the same name.

amaury_bouchard · 5 days ago
Thanks for the list! Yes, the name is used by several projects. Mine is the browser navigation one — hopefully the "µJS" name, the .org domain and the use case make it distinct enough.
stanfordkid · 5 days ago
I'm curious to understand, why would you build your website this way vs. say jQuery. I've never really understood the HTMX ecosystem. Is this just to avoid javascript and replace that with html pages, id's and attributes? It feels like the DOM is a very clear abstraction and scripting is a more powerful way to manipulate it. What do people like or prefer about this approach and paradigm?
amaury_bouchard · 5 days ago
The target audience is developers who are efficient at generating HTML server-side using an MVC framework, a template engine, whatever they're comfortable with. Instead of building a JSON API and a JavaScript layer to consume it, you just return HTML fragments from your existing routes.

µJS handles the AJAX call and swaps the fragment in the DOM. No JavaScript to write, no state to manage client-side. jQuery is more powerful and flexible, but that flexibility comes with complexity: you have to write the fetch call, handle the response, find the right DOM node, update it.

µJS does all of that declaratively via HTML attributes. It's not for every use case — highly interactive apps (think Google Maps or a rich text editor) still need proper JavaScript. But for the vast majority of web pages, server-rendered HTML fragments are simpler, faster to develop, and easier to maintain.

freakynit · 5 days ago
What happens when a user lands directly on 2nd or 3rd level page and have not "jumped" from 1st level page?
lofaszvanitt · 4 days ago
JS ecosystem is bonkers. Do not look for logic where there is none.
logicprog · 5 days ago
I think the idea is that it's completely declarative, and the range of what you can do, being constrained to a set list of operations and targets, becomes clearer and easier to understand?
josephernest · 5 days ago
Nice project, always interesting to see HTMX-inspired frameworks.

If you want something even more minimalistic, I did Swap.js: 100 lines of code, handles AJAX navigation, browser history, custom listeners when parts of DOM are swapped, etc.

https://github.com/josephernest/Swap.js

Using it for a few production products and it works quite well!

amaury_bouchard · 5 days ago
Nice! 100 lines is impressive. µJS is a bit more feature-rich (patch mode, SSE, DOM morphing, View Transitions...) but if you need something truly minimal, Swap.js looks like a great option.
panny · 3 days ago
I tried this out today and it doesn't seem to work for me, because it only considers relative urls to be "internal." If the urls are full urls (https://domain:port/...) then it won't work. It seems like it should be able to use window.location to work out that these full urls are also "internal" as well.
amaury_bouchard · a day ago
Thanks for the report! This is now fixed in v1.4.4.

µJS now resolves URLs using the native URL constructor and compares origin against window.location.origin. This means all same-origin URLs are correctly recognized as internal: - Absolute URLs: https://domain:port/page - Relative URLs: ../page.html, page.html - Local paths: /page (already worked)

Hash-only links (#section) are intentionally left to the browser for native anchor scrolling.

amaury_bouchard · 3 days ago
Thanks for the report! Using fully absolute URLs for internal links is not a very common pattern, and this hadn't been raised before. That said, it's a valid and interesting use case, I'll add it to the roadmap. In the meantime, using root-relative URLs (starting with /) is the recommended approach.
gaigalas · 5 days ago
I like the idea. DOM morphing is nice.

I've done this previously with morphdom to AJAXify a purely server-driven backoffice system in a company.

I would love something even smaller. No `mu-` attributes (just rely on `id`, `href`, `rel`, `rev` and standard HTML semantics).

There's a nice `resource` attribute in RDFa which makes a lot of sense for these kinds of things: https://www.w3.org/TR/rdfa-lite/#h-resource

Overall, I think old 2015-era microdata like RDFa and this approach would work very well. Instead of reinventing attributes, using a standard.

amaury_bouchard · 5 days ago
Thanks for the feedback! The RDFa `resource` attribute is an interesting angle I hadn't considered. That said, µJS's default behavior actually requires zero `mu-` attributes; it intercepts standard `href` links and `action` forms out of the box. The `mu-` attributes are only needed when you want to override the default behavior, use patch mode (updating multiple DOM fragments in a single request), or trigger requests from elements that are not links or forms. So in the simplest case, a single `mu.init()` call is all you need. That said, if you want something even more minimal, htmz takes that philosophy to the extreme.
0x20cowboy · 5 days ago
Not exactly what you’re saying, but a bit closer. With this library you set what css classes on the page are “hot”, it fetches the next page state and replaces that part of the page with the new state: https://github.com/robrohan/diffy