Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
VanJS – A no-JSX framework based on vanilla JavaScript (vanjs.org)
161 points by ms7892 on Dec 20, 2023 | hide | past | favorite | 168 comments


After reading the tutorial at https://vanjs.org/tutorial I was about to open an issue suggesting that they include constants with the SVG and MathML namespace, so that this:

    const {circle, path, svg} = van.tagsNS("http://www.w3.org/2000/svg")
    const {math, mi, mn, mo, mrow, msup} = van.tagsNS("http://www.w3.org/1998/Math/MathML")
Could be expressed like this instead:

    const {circle, path, svg} = van.tagsNS(van.SVG)
    const {math, mi, mn, mo, mrow, msup} = van.tagsNS(van.MathML)
... then I looked at the code at https://cdn.jsdelivr.net/gh/vanjs-org/van/public/van-1.2.7.m... and realized that adding those constants would increase the size from 1729 to 1806 bytes - a 4.45% increase for a feature most people would never use.

This thing is tight!

Just for fun, I ran their minified code through ChatGPT to see if it could deobfuscate it. It did a pretty good job of guessing sensible variable names for things: https://chat.openai.com/share/9dac0b13-7b24-409a-9cc1-494667...


Oh wow, didn't know ChatGPT could de-obfuscate code!!


It does a good job to give some analysis on malicious code snippets


This is where most libraries are going.

Svelte 5 builds upon these signals / derivations as well, and I guess a bunch of other libraries.

It would be time for them to get together and standardize on a signals library at this point that could be adopted to be a web standard and help interoperability between web components built with different frameworks.


I fully agree with the sentiment, but Svelte does not seem like a natural leader on that front since their version is a JS superset. Already a bit off the map for interoperability.

Solid's attempt feels more promising since it's building on existing native functionality (proxies / getters), and has intuitive, low-footprint usage (just function calls and reference comparisons).

Edit: It seems I'm not up to speed in fact, I hadn't seen Svelte 5 and runes yet, which are not compile-time. I will need to give it another try. I agree in general that signals and signal-based APIs are the future and definitely better than hooks at least, and a single agnostic standard would be good for everyone.


I tried solid and I didn't find it intuitive. Having your data behind proxies introduces more ceremony when splitting / combining props. I felt I was just moving the trade-off React makes when feeding a deps list to hooks to a different location. The proxies also made things hard to debug and you have to be careful about the language features you use with them.

I also have often have smaller deps lists in React's useEffect when wanting to call a callback on the props because of some local state changes. I wouldn't want the effect to fire because the callback changed however. I'm not sure solid even supports that in its createEffect since it is all automatic.


MobX and chill. Works with React, Vue, Solid, and etc.


I don’t know why they’re selling lack of JSX as a feature. React works without JSX too. It’s just (very convenient) syntactic sugar.


It is really an odd flex. As soon as you need a transpiler, which many modern JS devs are going to want and need for the benefits they provide (Typescript being huge) then using JSX seems a benefit not a problem to solve.


Not needing to use a transpiler is genuinely the thing that excites me most about VanJS.

If you're not a daily JavaScript developer, any form of transpiler / build mechanism seems almost guaranteed to break in the gaps between when you are working on a specific project.

The projects I have that are transpiler/build free are SO MUCH more pleasant for me to intermittently hack on!


Sure, I think that's a very real consideration, but if you're not a daily JS developer, I'm not sure why you'd use a function-call syntax to build up a template either. Something like Alpine.js where you just mark up your server side HTML is a lot easier than putting a bunch of logic into JS to build up a template. Or you could use lit-html or a million other things. This is an idea that's been done a million times but never catches on because it's not better than the alternatives.


FWIW, for HTML template:

1. How easy is it to support loops? 2. How easy is it to compose (defining small components that can be used in larger components)? 3. How easy is it to make the UI reactive to client-side state changes?

> because it's not better than the alternatives

What is the alternative you're refering?


I use TypeScript exclusively over Javascript so being pure JS is a con not a pro to me.


The library does have Typescript definitions available to use.

https://github.com/vanjs-org/van#typescript-support


…you can use React without a transpiler. Without JSX React’s syntax is pretty much identical to this.


Have you used esbuild? It’s a transpiler without the faff.


I've used it via Vite, and it worked fine.

I did write myself EXTENSIVE notes such that when I return to that project in the future I have a fighting change of figuring out how to build it again: https://til.simonwillison.net/github-actions/vite-github-pag...


You can trim down those notes a lot, like instead of `npm install`, I write a shell script called `script/prepare-env`, instead of various scripts written in a package.json file, I've standardized on `script/start`, `script/build`, and `script/deploy`. Now, no matter the language or package manager used, my workflow is the same.

And you could clean up the GitHub Actions file by just passing the base url to your build script, IE `BASE_URL="/${{ github.event.repository.name }}/" script/build`.

I mean no offense, these notes aren't that extensive, and most of the content is for setting up GitHub Actions / GitHub Pages. npm install / npm run dev / npm run build aren't very esoteric, and can all be documented inside the repo.

I'm actually a little shocked to find out you're a co-creator of Django.


The title of that page is "Serving a JavaScript project built using Vite from GitHub Pages" - that's what those notes are meant to cover, the Vite part is a subset of what I learned in that particular TIL.

Thanks for the BASE_URL environment variable trick - that was very non-obvious to me! I found the documentation for that just now: https://vitejs.dev/guide/env-and-mode

I'm not sure what you mean by "trim down" those notes - if anything I'm always looking for ways to bulk this kind of document up, in order to capture as many of the details of how things work as possible.

I tried the script/x pattern for a bit but I've found that Justfiles fit my brain better - I have a TIL about those here: https://til.simonwillison.net/django/just-with-django


JSX does seem to fit that niche better than any other alternative I've tried. It pulls off a rather remarkable trick of avoiding the "uncanny valley" that you usually get from starting with a language and contorting it to do something different.

JSX manages to feel just about right: it's plain-old-HTML when you want it to be, and plain-old-Javascript when you need an out.

Still... I wouldn't mind writing in a truly standard language. You're "really" writing the DOM, and HTML and DOM are this godawful bastardization of a user-interface description language. I'd be slightly less grumpy to have a good DOM-writing library, and deal with its ugliness, rather than mix-and-match the domain-specific-language in with my actual coding language.

I'd written myself a good Java version of this type of thing, and I found it kinda pleasant -- especially since it let me do HTML structure checking via the Java type checking. Typescript is -- God help me -- actually a pretty good language, and I'd be OK with actually writing my user interface structure in it, with the help of a good library.

At least then I'm writing in only one language. That saves me time to go out and learn the godawful bodge of CSS to make it look pretty.


Jsx never achieves plain-old-html in my view. <div /> is legal but <br> isn't.


<br /> is which matches xhtml (if not html5)


Html 5 has displaced XHTML for decades. And notably, XHTML is not html. <br /> is valid html5, but the trailing slash is meaningless. In JSX, it's required.


XHTML5 is a thing.


This is news to me. From what I can find (which is not much), it seems like XHTML5 is a further constraint on HTML5. So legal XHTML5 is legal HTML5.

https://en.wikipedia.org/wiki/HTML5#XHTML_5_(XML-serialized_...

If that's true, that means <div /> is not valid XHTML5 because 1) trailing slashes don't close tags in html and 2) div elements require a closing tag.

And if that's true, then it's still different from JSX. But this XHTML5 thing might be surpassing my comprehension.


> So legal XHTML5 is legal HTML5.

Almost. You would need to make sure to write polyglot markup [1].

> And if that's true, then it's still different from JSX

Absolutely.

[1] https://www.w3.org/TR/html-polyglot/


Writing the dom cleanly with code with native data structures using clojurescript's hiccup is definitely an improvement.


If you like JSX, good for you. But tbh it's neither necessary nor the best solution...


JSX is still a dependency with different variants which can cause issues. For example, SolidJS uses JSX but a different variant of JSX, which is a bit more slimmer (innerHTML instead of dangerouslySetInnerHTML).

I experimented with VanJS integrating into Astro. No plugin was needed & getting the SSR HTML is as simple as calling `.render()`.

I wrote a fork of VanJS called relementjs & took this concept further, using `.toString()` instead of render. Now SSR components can be rendered inside a template string.

` <div>${my_component()}</div> `


JSX is redundant, at best. VanJS demonstrated ordinary JavaScript syntax is as good as JSX, if not better. How can something feel sugar-ish if it's actually more verbose than ordinary JS code? Plus, you can't really execute JSX code in browser's developer console.


> How can something feel sugar-ish if it's actually more verbose than ordinary JS code?

I personally find XML much easier to parse than a lot of nested callbacks and I especially like that content is always inside tags, rather than another argument as part of the library's functions. p("Hello, world") is readable enough, but as soon as you start adding html attributes, it becomes less legible to me:

li(a({href: "https://vanjs.org/"}, "VanJS"))

vs.

<li><a href="https://vanjs.org/">VanJS</a></li>

I'm certain some people probably prefer the former over the latter, but I much prefer how JSX/XML reads. I can immediately tell where the content starts and in formatted code, I think it's a lot easier to see visually where things start and end, because of closing tags, instead of just a parentheses.


tbh, you don't need JSX to know where things begin and end (if this is something really important for you):

li(a({href: "https://vanjs.org/"}, "VanJS")/*a*/)/*li*/


As a long time backend developer and new front end developer, I gotta say I find JSX to be downright wonderful.

There are some oddities with react but wow jsx makes my code clean and easy


Build simplicity. It's nice to work with functions all the way down. VanJS could add JSX on top of it though.


There is some plug-in that adds JSX support though.


React is also functions all the way down and the syntax is pretty much identical to this


Feels weird to seem to seriously represent your framework as being about size without including a comparison to preact - the obvious choice for a react developer looking to reduce their footprint.

Preact claims 3kb on their site, and for a 3-4x savings you're going to need to be fairly compelling. I don't mind paying a modest cost for 10x or 100x improvements, but (call it Stockholm syndrome) I like react syntax


Modern reactive UI frameworks get away from maintaining virtual dom. If you want minimalist vdom preact is a great choice. If you'd like to part ways with vdom while keeping jsx, try svelte or solid-js


Preact has signals now too if you're into that.


What are the long term theoretical benefits of vdom? Concurrent rendering?


At least with solid, you can't do `const a = <jsx>` without caveats. Instead you have to store it as `const a = () => <jsx>` (which isn't quite the same, having problems managing state/renders). In React I was optimizing things by storing vdom in state/memo & then interpolating that exact vdom. Or sharing vdom in multiple places. Without vdom you can't throw jsx results around so haphazardly

In solid there's also managing batching updates to reduce rerenders since it eagerly updates dom. It feels like one needs to be more meticulous


The flipside of this is that in React you are paying the cost of active vdom all the time, even when it doesn't pay off by optimizing large batched updates. In Solid, you sometimes have to optimize for some eager updates, true. That will have to be done at some point, though, when the situation calls for it - in either a specific or a general way. It feels natural to shift that optimization to the state layer, and completely eliminate the need for vdom, at the expense of some more thought and active, upfront work. It's a similar tradeoff to spending more time crafting static typing that works well, versus forgoing static types. The tradeoff seems to have good effect too - Solid seems much faster than React both on paper and in practice (my personal experience).


Your comment signals a key misunderstanding.

Doing a=jsx and interpolation does NOT touch the vdom in any way.

VDOM only comes into play for jsx returned from components before being rendered to screen.


People complaining about lack of JSX, HTML angle-brackets: I don't get it.

XML gets all the hate for being overly verbose (and for good reason [1])

however with HTML syntax, which is almost the same, everybody seems to be just fine...

[1] XML is sooo nineties, the "modern" developer uses markdown. inb4 S-Expressions

</rant> (...yes I know)


It's a little more than that.

Some people feel that XML is great to represent nested structures/trees, and that trying to do that with code is less readable.


For me it's about not to learn yet another new syntax, and HTML is here to stay.


If you know JS (or any other C family language), the syntax is not new.

It's about getting rid of a flawed syntax.

But it'll probably never happen because people are used to it and oblivious to change.

Unless of course if one of the FAANG publishes it as their New FrameWork [tm], then everybody will jump on it, and people that don't are seen as "not modern".


Agreed. JSX is a bug, not a feature.


... and imposes a build step, for no good reason.


The preact team also dislikes transpiling jsx so they've developed an alternative using tagged template literals: https://github.com/developit/htm


This looks great! I love having the ability to bring existing React/JSX knowledge and be productive with VanJS, while also ditching the transpilation and build tool wildness that has afflicted the ecosystem.

React and JSX were needed in 2013, but with the power and convenience packed into new ES standards and supported by modern browsers, React and JSX should be viewed as bloated polyfills at this point.

It’s really heartening to see things trending back toward single script tags that help you avoid build tools, churn, and dependency hell. To that end, I’m leaning toward htmx for my own project, but what you’ve come up with looks really enticing as well.


It's the CIRCLE OF FRAMEWORKS!

Dev: "All the legacy crap is too bloated obnoxious to use and has to much overhead, packaging, and training required to do even smiple things. I'm going to invent a simple lightweight framework from scratch that uses first principles to stay clean."

Time passes framework starts to get used

Random user: "I need to be able to dynamically modify table spacing in Elbonian based languages, whilst allowing for non-standard viewports to dyanmically load partial DOM changes while asynchronously displaying a cat falling over gif animation and the only way to do it in the framework is a bloated hack, any serious enterprise tool needs this, until you get it this is just a toy"

Dev: "Well I guess that makes sense"

Time passes: Hundreds of these random edge cases pop up, og dev is tired, the project gets moved to a committee, usually some drama pops up for several reason that have more to do about posturing than the framework itself

New dev: "This framework is bloated complicated and has too many dependencies and layers of abstraction. After all what purpose on earth is there for me to have to configure whether or not I am using Elbonian derived language encodings that nonsense. I'm going to write my own framework that is lightweight and simple and does what I need it to without all this extra crap"

IT'S THE CIRCLE OF FRONTEND!!

AND IT MOVES US ONWARD!!


Previously discussed 7 months ago (204 comments): https://news.ycombinator.com/item?id=36067983


Love the use of functions to craft components. Such an undervalued way of writing a UI.


clojurescript and elm would perhaps interest you =)


I have struggled to use Elm because I rarely if ever need what it offers in my personal projects and at work we are a Lit shop.

Love CLJS though, I use that sometimes for personal work when I have an urge to lisp.


The library looks nice dx wise but complete lack of any lifecycle hooks is a bummer. I get that minimalism is the core theme but to use any js ui lib that is not explicitly built around the same reactivity model you need to hook into component lifecycle.


Life-cycle hooks typically run as side-effects from state changes, so that would be your route to do something similar.


If I want to do something like initialize codemirror when my component attaches to dom, I need an event/hook for that. I can't do that solely based on state subscription?


This kind of reminds me of Mithril, although I don't really have much experience of that either.


One huge benefit to VanJS is it's simplicity. It is far easier to fork than most other UI component libraries. I did it writing relementjs & rmemo. I encourage others to fork it as well. VanJS is well written. You get a reactive state management library (on the browser side) & a UI renderer in < 150 LoC. Very impressive. I encourage anyone interested to experiment with this. It has been very fruitful for me.

In case anyone is interested, here are my forks.

https://github.com/relementjs/relementjs

https://github.com/rmemo/rmemo, which exports https://github.com/ctx-core/ctx-core/tree/main/rmemo. Note that rmemo also supports server side reactivity.


This is cool, though I think a table-stakes example that is missing is how to do a network request. I see the stargazers example but that entire component is awaited, which doesn't mirror the common case of async fetch in response to user input, in which the response is fed to sub-components.

The stargazer example leaves me with questions like- are components both async and non-async? What if the component re-renders due to other state changes, is my network request fired more than once? Do I have a "component coloring" problem where once one subcomponent is async, the entire parent hierarchy has to be?

Im sure there's answers to these questions if I read the docs, but as a curious passer-byer an example mirroring this common ui pattern would answer a lot of questions for me!


Is this is what you are looking for? https://vanjs.org/vanui#await


Hmm, maybe! Is that the idiomatic way to do async? I was thinking something along the lines of this, which react devs will have written a variation of plenty of times :)

    import { useEffect, useState } from "react";

    const GithubUser = ({ login, avatar_url, html_url }) => (
      <div>
        <img src={avatar_url} style={{width:40, height:40}}/>
        <a href={html_url}>{login}</a>
      </div>
    );

    export function App() {
      const [stargazerResp, setStargazerResp] = useState([]);
      const [repo, setRepo] = useState("vanjs-org/van");

      useEffect(() => {
        fetch(`https://api.github.com/repos/${repo}/stargazers?per_page=5`)
          .then((r) => r.json())
          .then((r) => Array.isArray(r) ? setStargazerResp(r) : setStargazerResp([]));
      }, [repo]);

      return (
        <div className="App">
          <input value={repo} onChange={(e) => setRepo(e.target.value)} />
          <ul>
            {stargazerResp.map((s) => (
              <li key={s.login}>
                <GithubUser
                  login={s.login}
                  avatar_url={s.avatar_url}
                  html_url={s.html_url}
                />
              </li>
            ))}
            </ul>
        </div>
      );
    }


You can do with VanJS in this way as well. `set...` functions in React map to `....val = ...` in VanJS (which I think is more intuitive). But the `Await` component is a good abstraction so that you can specify what to show when the resources are being fetched and what to show when there are errors while fetching the resources.


Would we expect the list to re-render once the fetch finishes? This may just be years of react poisoning my brain

    const Stargazers = () => {
      const stargazers = van.state([]);
      fetch(`https://api.github.com/repos/vanjs-org/van/stargazers?per_page=5`)
        .then((r) => r.json())
        .then((r) => stargazers.val = r);
      
      return ul(
        stargazers.val.map((s) => li(s.login))
      );
    };


Yes. This is how VanJS works. Code with VanJS is often more concise compared to equivalent code with React.


Ah ok! Very cool. Maybe I'm still missing a tiny piece of syntax? I don't see any output when I run that code in the fiddle

https://jsfiddle.net/397fb684/


Ah.. you need a binding function to wrap the state-derived content into a reactive UI element, see: https://vanjs.org/tutorial#state-derived-child. I made the change in your code to make it work: https://jsfiddle.net/rkfmpx06/1/


Oh! I understand, thanks for walking through that. Yes very terse compared to the equivalent react :)


State management seems a bit counter intuitive at a glance.

I found this statement to be confusing; https://vanjs.org/tutorial#state-val-is-immutable While you can update State objects by setting the val property, you should never mutate the underlying object of val itself.

Then beneath is an example of the following;

   const text = van.state("VanJS")
   ...
   input({type: "text", value: text, oninput: e => text.val = e.target.value})

Which looks like a mutation - after reading a bit more around it is clearer that .val has a setter; but at a glance it just isn't obvious what is happening which I feel isn't intuitive.


States are mutable whose value can be get/set via the ".val" property. However, the value of the states should be (ideally) an immutable object (or primitives).


Yeah I understood that better, thanks.


It's too late for me. I, like Douglas Crockford, am completely done with JavaScript [1].

HTMX, LiveView, WASM, Dart, ClojureScript, Kotlin, ScalaJS, Dioxus -- There are lots of options folks. We don't need to surrender to the tyranny of JavaScript.

[1] https://www.youtube.com/watch?v=lc5Np9OqDHU


does it mean I have to add many things that are already in vuejs and react if I decide to use vanjs? what vanjs really brings to the table? the existing SPA frameworks are indeed super heavy and anything light and can get daily work done easier will be nice.

after trying react for years I now switched to htmx with a simple backend, until there is a simpler to learn and use SPA(no SSR please) somewhere.


A lot of the heft of React comes from its optimization and reconciliation code. Looking at the examples on the homepage, I’m struggling to see how this framework would efficiently make updates. It seems like it would just need to update the entire component tree down on any update. For any sufficiently large app this becomes very slow very quickly.


VanJS is a lot faster than React: https://vanjs.org/#performance. UI re-rendering in VanJS is kept at the local level as much as possible, which can be achieved without even the need of vdom


I love it. This feels a lot like Flutter's UI system (which I also love). Anything to move us away from JSX is a win to me.


The whole “small footprint” thing is always weird to me. I understand that lots of people really want to squeeze efficiency out of every single kb but Im struggling to see where this is helpful. A single image, a single pixel from the marketing team, the actual application code, the page, etc are all going to dwarf the framework.


Coming from living in third world countries where I had to ration data transfer, at speeds ranging equally from 50kbps to 1mbps, I appreciate these efforts greatly


The framework is fundamental for all the rest of the stuff. You transfer it before the majority of the site's content. The faster it arrives, the faster your site is usable. It's also always in use. A badly sized image can cost a few MB, but that's a trivially addressable problem on one page of your site. The framework being 10x its required size is a huge development hurdle to overcome.

Your local mom-n-pop laundry place's website probably doesn't need these types of efficiencies, but if you're running a large ecommerce site, time-to-interactive is an important metric which can cost a lot of money.


I worked at a startup where we reduced the app size by moving to preact from react. The thinking was that it would be beneficial for the low powered mobile users but what we actually saw was increased retention and engagement from all segments as the app was a lot more snappier and didnt chug on every click/tap


I had until very recently a limited data plan for my cell phone in Europe, a plan similar to the one most of my friends have. The only websites that remained reliably accessible once I'd hit the 2Gb limit were HN and i.reddit.com (RIP). 90% of all other websites just time out.

If anything, I wish we could force the marketing team to compress their images better.


Tangetial: I'm not a huge dwm/suckless guy, but I have it on my Raspberry Pi. I love how instantaneous everything feels. Compared to 14-core, 32GB work laptop running Windows which I will forever loathe because how slow and buggy everything is. Same for websites (e.g. HN, libreddit vs reddit etc.)


It’s about execution cost, not size over the network.


I think this is why, for me, the future of JS are resumable frameworks like Qwik and Marko 6 – you get all the benefits of NextJS + React (composable UIs, state management, server-side code, etc) with no hydration, which is a big cause of sluggishness with web apps.

Your code only executes when needed, and only that specific chunk of functionality.


Not necessarily. Lots of jpegs go below the 1 mb mark (and tons of framework are bloated like crazy)


I think you misread the title. The framework is 0.9kb, not 0.9mb.


> 道可道,非常道

> (A rule that can be told by words, is not the rule that should universally apply)

I'm curious where the dev got that translation or if they made it themselves. I have translated it before like "No method that can be explained is universally applicable" but I haven't seen many other translations like that.


Here is the break down of my understanding about the philosophy described in 道德经:

1. There might be some universal rules about the world. 2. There are limitations in human's understanding and human language. 3. For all the rules that can be described in human language (which is subject to the limitations in 2), they can't hold universally.

Putting into the context where the words are cited. Any claim (in human language) about programming like "you should program in this way" is most likely not universally sound.

Of course a philosophy book 2000+ years ago can be interpreted in a different way. Happy to see the elaboration of your understanding "No method that can be explained is universally applicable".


The word dao literally means “road” but by the time of the DDJ, it had come to mean “way of doing something” and “speech that explains how to do something”. Interestingly, in Greek “method” is meta + hodos and hodos means “road” so it’s an etymologically appropriate translation.


道 (dao) means "the way of doing thing" in modern-day Chinese as well. For instance, 王道 means the "the way of governing". 路 is more common for the literal meaning of "road", though 道 has a meaning of both "road" and "waterway" as well.


I wanted to try a vanilla JS version of the VanJS counter: https://jsfiddle.net/pdfzx3a9/

Have people forgotten about event delegation? Surely the number of event listeners can get excessive in some cases.


I think this looks great and I'm excited to give this a shot.

Also, shameless plug for my own micro JS UI library. No signals, just regular JS variables: https://github.com/kevinfiol/umai


The syntax looks similar to dom-proxy [1]

[1] https://github.com/beenotung/dom-proxy


This looks great. But I replaced my last JavaScript use-case (fading image slideshow) with a css solution today so you will have to make this exciting journey without me.


How is this different from: https://github.com/jorgebucaran/hyperapp



Looks awesome


You can just use Mithril.

https://mithril.js.org/


This doesn't look too bad for pushing down minimal dom manipulation, since it includes reactivity

Looking at my own use of solid, things this is missing are:

- jsx

- stores (having some way to hook into van's reactivity would address this, maybe digging into van.state internals there's a way)

- onMount/onCleanup

- For/Index/Show which helps handle efficient reuse, this could probably be built as a layer on top of vanjs


> jsx

I don't agree, this syntax for html is much nicer than JSX when you get used to it. Converter from HTML at https://www.solenya.org/convert


The author seems to be chasing the small size trophy at any cost...to the point where he's using "var" instead of "const" because it saves a few bytes. Not indicative of someone serious...


The author works as a back end engineer @ Google. I appreciate his "outsider" approach as it is quite a bit simpler than other UI libraries & even reactive state management libraries such as Nanostores. I'm sure if he were in a different development role, his technique would be refined to account for full stack development use cases.

I don't mean to throw any shade on Tao. He is very talented & his approach is beneficially novel. I just don't think he as much experience what life is like as a full stack (or front end) developer as many here.

I forked his work to create relementjs & rmemo. Tao didn't want to support server side reactivity & I heavily use it. Also, Andrey (maintainer of Nanostores) didn't want to add my PR for autosubscriptions due to the complexity cost that would be added to Nanostores...So I wrote my own set of libraries. I hope library simplicity trends more because it is very nice to work with simple libraries.

Tao's approach is sound. He just relies on the DOM to manage the reactive state, so SSR reactivity would require a different approach. I ended up using WeakRef, which is significantly smaller than the approach used by traditional reactive state management libraries.


Where is your repo link?


No offense. What exactly is the cost of using "var" instead of "const"? You seem to pretend it to be a big deal.


It sacrifices maintainability and robustness to save a measly 80 bytes (I counted).


Why do people dislike JSX? For me the lack of JSX is a negative point. Why would I want to write nested functions everywhere? JSX is HTML on steroids, but it can be just HTML if you wish it.


Why would you insist to write the function name both before AND after the call?

<DoStuff> args </DoStuff>

vs

DoStuff(args)


Is this the main argument? I think the simplest answer is boundaries. Taking into account how much nesting is involved in writing HTML, it's a clear benefit having a named boundary vs a closing parenthesis.


> Taking into account how much nesting is involved in writing HTML

which is another one of its flaws.


What would the alternative be? How can you design a UI without nesting things?


Too much nesting usually signals that you should decompose it to smaller components.

Same as with regular code and functions — overly deep nesting screams bad code.

Honestly, JSX is just a verbose way of calling functions / constructing objects, which is probably OK if it was a language of its own (XML/HTML) — and even then, JSON/YAML won over XML — but makes little sense being embedded in a language which had functions & object literals from the get-go (JS)!

The only reason JSX exists is because it was a cool marketing feature to convert existing PHP/HTML developers to React. And React succeeded mostly because of that. Not because JSX is cool technology-wise (it isn't). But because it hit the right spot in the right time.


Well said! You got the point.


Not to me, it isn’t. It aligns with how the box model itself works.


More like:

<DoStuff args={...} />


Please no

The {...} syntax is a non-standard extension to XML/HTML I think MS came first up with back in the day for XAML (WPF).

It's a hack to "fix" a shortcoming of the base language.

It does not compose, i.e. :

<DoStuff args={<DoMoreStuff args={...} />} />

does not work.


That would be

<DoStuff args={...}><DoMoreStuff args={...} /></DoStuff>

Still more readable than brackets hell like:

DoStuff(DoMoreStuff(DoEvenMoreStuff()))


^^^ Apples to oranges:

    <DoStuff args={...}><DoMoreStuff args={...}> <DoEvenMoreStuff args={...}/> </DoMoreStuff> </DoStuff>
vs

    DoStuff(DoMoreStuff(DoEvenMoreStuff()))
Both of them you would split into indented lines when they become too long. And 1 becomes too long much faster.

Also with 2 you can do

    const todo = DoMoreStuff(DoEvenMoreStuff());  
    DoStuff(todo);
where as with 1 you cannot.


With JSX you can do

  const todo = <DoMoreStuff args={...}><DoEvenMoreStuff args={...}/></DoMoreStuff>;
  <DoStuff args={...}>{todo}</DoStuff>


<1KB is great and all, but part of the problem is that as soon as you add render blocking JavaScript you're adding a network round trip and JS on the critical path. <1KB might not feel like much, but it's not going to be very different than 10KB. Once you throw in non-trivial product code it doesn't matter at all.

Either you should statically/server generate your site, or you have rich content that needs a client side app and you should pick a framework with rich features.


If you're worried about an extra network round-trip, <1KB is small enough that you could inline the entire framework into the <head> section of your document.


Frameworks need product code.


With bundler like esbuild, all JS code, including 3rd party libraries can be bundled into a single file. You don't need an extra round trip for a library.


Is there a reason they compare bundle sizes with for example jquery when every framework ends up importing jquery anyways, since animations are a headache and are often readily available already with jquery?


> when every framework ends up importing jquery anyways

Evidence?


we're coming full circle to $('<div>').appendTo(parent)


Not really. That kind of code makes it easy to render something initially, but absolute hell to update and keep your state and UI in sync.


This is unacceptably huge compared to the similarly named Vanilla JS.

http://vanilla-js.com/


> This is unacceptably huge compared to the similarly named Vanilla JS.

It took me a minute to get it.

0 bytes uncompressed, 25 bytes gzipped


Can you enlighten us?


It's just regular, already-in-the-browser, JavaScript. 0 bytes to download it.

25 bytes is the size of a gzipped empty file.


gzip has a fixed header, hence the 25 bytes


Than you


Try to replicate a sample app made by VanJS with Vanilla JS and check the difference


challenge accepted (app #2, since #1 is just static), BEHOLD:

  <script>
  const Counter = () => {
    let counter = 0;
    const updCounter = (delta) => {
      counter += delta;
      document.getElementById('count').innerText = `♥ ${counter}`;
    };
    document.getElementById('up').addEventListener('click', () => updCounter(1));
    document.getElementById('down').addEventListener('click', () => updCounter(-1));
    updCounter(0);
  };
  Counter();
  </script>
  <span id="count"></span>
  <button id="up">*thumbs-up*</button>
  <button id="down">*thumbs-down*</button>
15 LoC vs 7 LoC, zero dependencies vs. 0.9kB gzipped or 2.7kB minified. Additional learning curve: 0h vs 1h (allegedly).

Honestly, I am biased and think Vanilla JS wins :D


The fact that you have to manually maintain the binding between states and UI elements and propagate state changes to UI elements is exactly the thing offered by VanJS, or other popular reactive frameworks (despite with a much larger bundle size)


Sure thing. But it's also one of those things that comes back to bite you when you least expect it: https://vanjs.org/advanced#why-not-dom-valued-states


I don't get you. Could you elaborate?


In the example I linked, the VanJS state cannot contain an element reference.

This would lead to unexpected results. So you have to be careful with state management, regardless of whether it's automated or not. There are similar pitfalls, including performance issues, with any state management system, hence dedicated state management solutions like Redux exist to address this.

The core of the argument is that complexity sometimes cannot be avoided. You can quickly wind up with moving this complexity elsewhere, e.g. by pulling in an additional library dependency. This results in having to learn, master and manage additional dependencies. Whether that's fundamentally better than the explicit and straight forward way depends on the scope of the project and its specific requirements. There's no silver bullet in any case and pros and cons with every approach.


Tbh I can't follow your logic here. You mentioned that in VanJS there are things that need to be careful with. But the same is true for other frameworks, even for plain Vanilla JavaScript. Thus what exactly the point that you're trying to make?

Complexity can't be avoided for extremely complex use cases. But that doesn't mean a simple solution that can work for most of the use cases has no value.


Elements with an id are globals, so you do this (not that it's recommended):

    count.innerText = 
    up.onclick = 
    down.onclick =
etc


Technically, every element that's not in the shadow DOM is a global. But yes, there's plenty of ways to implement this and an argument to had for most of them.


Right, I should have clarified that they're properties of the window object.


Magnitudes ...


Why do people even reinvent this wheel every few months? Does the <1KB size even matter with modern caches and HTTP2? Does the amount of written lines of code matter when there's modern linters and tooling?

Anyone can build a "reactive" type framework by following tutorials online now, they're super in vogue.

Is my cynicism here warranted, or am I just jaded from 20 years of watching pendulums swing left and right and watching the wheel be reinvented over and over?


You're jaded. Look at this as someone completing the exercise to understand reactive frameworks by implementing a toy implementation. Like when someone implements yet another lisp. Maybe you disagree with it getting to front page, but then don't upvote & move on. There'll always be some amount of front page content someone considers crap & it's best if we don't collectively fill every comment thread disparaging about it


> Look at this as someone completing the exercise to understand reactive frameworks by implementing a toy implementation

I don’t think that’s the intention of the author or the person who posted this or the people who voted it to the top of HN


For privacy reasons, resources are no longer cached across origins, so the size does matter (though arguably 1KB more or less makes absolutely no difference on any webpage that has a medium-sized image, especially if you have SSR for the first paint).

But the better reason why these projects exist is for experimentation and iteration. It's not like everyone who uses React will switch to this over the holidays, but maybe in five years some of the ideas in here will become more mainstream, and a future version of React (or a different framework) may adopt them.


It is strange when this was rolled out as to why decentralizeyes like functionality was not targeted to be built into browsers enabled by default.


Because it's a different threat model: Separating caches per-origin prevents a site A from seeing what resources you requested on site B.

But something like Decentraleyes prevents site A from seeing what resources you requested on site A.

(Or rather, whichever CDN provider site A is using.)

You could have both at the same time, but they are orthogonal. As for why it's not in browsers, assuming good intent, I'd think it's because it requires you to bundle a whole lot of libraries with the browser for it to be useful as a local CDN. If browser vendors decided which JS frameworks are bundled with the download and which ones are not, I'm not sure if that would help decentralisation!


I think your cynicism is warranted for you. Like if you're not looking for a new framework and you are interested in the new, shiny JS refactor, don't bother. I live a happy life, using the same framework and never looking at all the other stuff.

But at one point our big contenders were also shiny and new, and I am glad someone less cynical than me was willing to put them to the test.


The amount of code matters when the lowest-performing Androids on the market have an order of magnitude less single core performance than a modern iOS device. V8 parse times have got much better over the last few years (they used to be a big bottleneck) but you’ve still got major execution cost leading to bad user experience.


The code size is a poor proxy for performance though. React likely has some areas which are much more performant just because of development efforts, even if it's larger. If we want performance, that's the metric that should be public and clear.


FWIW, VanJS's performance is very impressive. At least much better than React: https://vanjs.org/#performance


>Does the amount of written lines of code matter when there's modern linters and tooling?

So long as JavaScript is interpreted it matters a whole lot on mobile devices.

The amount of code delivered and executed on the device has a huge bearing on performance on lower end devices. Unless you're on some $1000+ phone and arguing from a place of privilege.


Let them reinvent it. You are not required to learn it right now. If it really manages to become widely used you can learn it then. If not well the guy is spending his own time on doing something he likes.


Size does still matter. Just because we _can_ serve huge amounts of data over the pipe quickly doesn't mean we _should_.


Except... the huge amount is photos and fonts and whatnot. As you can see on the vanjs size, the biggest framework Angular weights 85 kB and it will be cached. Compared to what Angular also brings in functionality, i don't see size of the framework as an argument anymore.

Consider Hackernews: hn.js comes with 21,4 kB. The Y logo on the upper left is a 46,32 kB SVG!

What should i care about 1kB minimal framework or 85kB all-included framework? Skillset, maintenance burden, compatibility matter.


> Consider Hackernews: hn.js comes with 21,4 kB. The Y logo on the upper left is a 46,32 kB SVG!

hn.js is just north of 5.2kB (2.3kB compressed), and the logo is 315 bytes.


Sure? I was looking in the network tab of Firefox.. Mhh, strange.


You should also be mad at all the people making game engines, compilers, llm's, and container hosts.


People feel unsolved problem for them and want a better solution for them individually. As you said “anyone can” so “anyone will”. Just ignore posts like this ;). After 20 years of experience just pay attention to v1 of anything :)


The problem is not size. The problem is installation, configuration, dependencies, transpilation, IDE setup, etc. And here you have the plain old notepad with browser - nothing else...


Web development makes me want to cry



Not literally


While literally literally does not mean figuratively, people often use literally to say figuratively with extra emphasis, to the point where some dictionaries literally⁰ list figuratively as a secondary meaning for literally.

Yet another example of language evolving through variations in common use that take hold.

--

[0] literally literally, not figuratively literally




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: