Show HN: I built a 4kb alternative to React, Vue, etc for building web UIs.
synergyjs.org
synergyjs.org
There's lots of avenues that can be taken with things similar to react - and trying and experimenting can create some better methods. It's not as if react or vue were built in isolation from all the previous software.
Wouldn't it be better if we experimented with radical new ideas for development instead of reinventing the same JavaScript wheel for 300th time?
Do we ?
As long nobody but you uses it you can add/rewrite as needed while keeping bloat to a minimum.
1) tradeoffs, preferences, exigencies of use-case
2) devs who conflate their specific use-cases with universal truths
3) fickle winds of fashion affects tech as much as any human endeavor. Right now jQuery is out and React is in
As for jQuery specifically, there has been at least 2 trends militating against it: a wider industry move away from patterns of direct DOM manipulation into virtual DOMs (i.e. using frontend frameworks); and a more widespread acceptance of functional programming techniques while jQuery is inherently "impure"
Using virtual DOM does not give much benefit if at all on modern browsers.
Functional programming is just one of many existing paradigms and not a silver bullet. One does not need to plug every hole with it.
I would note in passing that a description of industry trends is not an endorsement of them. There are lots of reasons for trends beyond pure rational technical strategy.
As for developer pool - when I need to hire subcontractor I've never had problems finding one with enough qualifications in whatever area I would need. Btw all subcontractors I've ever had are remote since year 2000
Not sure what particular insights. Well here is some example: state management. It is basically split into 2 parts. One part is the actual business object that matters and is a slice of full data on backend. It is a class with methods that take parameters and modify state when executed and broadcasting fact of said modifications so that the UI is aware. Executions of said methods also causing data exchange with the backend using JSON based RPC. Simple example would be Customer with get/set Name/Alias/DOB etc.
Then there are web components and those keep their own state that is limited strictly to presentation issue. For example Currently active form of the application.
It is a big area in general with lots of details and requires way more than a simple post to describe. Unfortunately we do not contribute to open source so can't really share any code.
So structurally the app is fairly simple. Some JavaScript files that are being merged when posting to production and backend server written in C++ accessible through JSON based RPC that can serve thousands of requests per second on single computer with few real cores without breaking much sweat.
When you take into consideration other languages and their ecosystems this was happening from the start. It is just evolution. If this lib gets enough traction and goes into the right direction, whatever that may be, it will become popular. Ofcourse chances are small, but..
Case in point with JS libs and the following line: "You are going to start adding modules and eventually you're going to have a whole ecosystem". This happens with individual JS-built products using libs, but definitely does not happen universally with most JS libraries. Take React, the first comparator referenced in the post title: its ecosystem is entirely 3rd-party addons and the core is focused and single-purpose to this day. They've added a lot of internal complexity to their vdom implementation which adds code bloat, but it isn't what this commenter is describing.
Vague comments like this never apply to all cases (often apply to no cases). It's a lazy comment that contributes nothing without being specific to this new library. You need to read the code and develop some genuine insight to have this type of commentary.
Yes. And that is _exactly_ the point. These anti-bloat libraries either die or become the enemy they set out to defeat. We've been to this rodeo before, yes?
Nothing wrong with that per se. But we gain nothing by pretending this cycle doesn't exist.
Perhaps a smarter approach would be: I've replicated 80% of X's features with 20% of the weight. Here's what's missing.
Then if Library X puts on weight, so can you, if you want. But feather weight in and of itself is not a benefit per se (especially when it's likely temporary). It's a feature. And too often we all gets sucked into the feature trap because we forget it's ultimately about benefits.
This idea puzzles me. It's not like the code suddenly breaks or the world changes so much as to render it completely obsolete.
It's borderline madness to keep chasing new and shiny things and then complain that things keep changing.
Stability is a feature, not a bug.
This probably isn't true, but more importantly this statement misses the point because you're bundling a bunch of stuff into the "anti-bloat libs" bucket with no consideration for the individual architectures of each.
To come back to the example above—React—the core react library is 3.2kb[0] gzipped. (and it hasn't died out). What I'm selectively omitting here is the architectural detail that React have separated their API implementation (core), their renderers (DOM, Server, Native) and their templating (JSX), all of which are loosely coupled optional components. That's a very effective approach to omitting bloat, and is a model that can be copied in an approximately compatible way (see e.g. the anti-bloat Preact and Inferno, further examples of anti-bloat approaches that have not "died out")
[0] https://unpkg.com/react@17.0.1/cjs/react.production.min.js
To be Clear; The submitted ShowHN is a welcome effort and espouses (imo) fantastic ideals and objectives. I'm glad that people like the developer take/make the time to contribute to the greater good of the open source community.
It is also great/fantastic/good/{some other positive superlative} that people like the submitter keep on fighting the good fight when it comes to program bloat.
While I'm not trying to read another persons thoughts here - Perhaps their comment was indeed meant as a 'oh another thing that's gonna end up bloated due to feature request creep' (which was my initial jaded reaction too although I just didn't think it was an opinion worthwhile posting as a comment that contributed something worthwhile to the conversation so didn't do so).
Call it a measure of the current global mood within the HN that the comment became the top voted comment.
As to what my point was? Uhmmm I forget.
Ecosystem-wise, it encourages integrating with high quality vanilla libraries directly instead of having a react-this or vue-that for everything under the sun.
This approach has worked well, IMHO.
I've tried mentioning it to friends in web dev, but they don't seem to be willing -- if they even look at it, they never try it out. At this point, I just shrug and go back to my simple, reliable and fast pages which load in KB instead of MB...
I've been working as a frontend developer most of my life, so I see what you're saying. Still, it's a just a fun thing to do. I've made some hobby libraries myself with no intention of it "catching on". And even if it's reinventing the wheel, perhaps one of the spokes is improved .
For example, Opera 12 (Presto) has full modern DOM support with .createElement and such, but I doubt your framework would work with it?
There are hundreds of browsers and engines out there, and I want to support them all. Would your framework degrade gracefully in a browser you've never tested before?
Also, how do you deal no-JS users? I read through the documentation, but could not seem to find one?
This is a noble and audacious goal, but I suspect that it would either take enormous resources, or the least common denominator approach.
OTOH supporting, say, the 3 major browser engines that serve > 99% of the audience is sort of achievable, and also practical.
b) I believe the remaining 1% matters, perhaps more than the others. (See wheelchair analogy.)
c) Yes, it does take a least common denominator approach at first, but you can build a lot on top of that, carefully, without upsetting even such classics as Netscape, Mosaic, and IE4.
Would you say that one developer working part time for a couple of years is "enormous resource"?
IE4 had an early DOM but it was different from the current one, and JS syntax was different too.
Writing DOM-modifying code that does useful things on IE4 as well as on current browsers is possible, but requires a lot of extra code and testing for little gain, and will encounter browser bugs if you do anything complicated, so you can't just drop in a thoroughly-portable framework, you need to test as well.
If what you mean is you'd like graceful fallback to a decent non-JS page on ancient browsers, that's doable, but then it doesn't matter if the JS framework only supports 99% of browsers. Just make sure to disable the framework on browsers too old to use it. (You'll still have a hard time with CSS on Mosaic.)
And yes, I did create something like this but I wanted to include also a router (like Mithril) and a standardized way to handle state, both global and local... and I ended up with https://h3.js.org -- yep, probably not many people use it but I have been using it for months for personal projects and I keep tinkering to improve a little bit whenever I find I need to polish some rough edges or support additional use cases.
Like others said, I wonder how long it will take for bloat to creep in. For now though yes, I agree that you really don't need much code to build a fully functional SPA. And creating your own micro framework really helps you understand how things really work, without relying on someone else's abstraction.
But, what would you say to those that say, “Great- another year, another JS framework”?
Specifically, why would I want to use this instead of Vue? What does it buy the average developer?
I find it kinda interesting too so I might try it for my next project...thats how it goes.
In this world of bloat, it is so refreshing to see someone doing The Right Thing.
A noble goal. I like that you are avoiding compilation and tooling.
Can you explain, what technique you use to update DOM (I'm lazy to walk through code base), is it proxy object like Solidjs, or something like "uhtml". How do you manage to dispose event listener internally (is memory leak possible around that?)
---
Just read some docs it seems like using Proxy!
I like the docs because, having come from a different dev environment, and seeing web/html as a vast sprawling landscape of surface area to learn, littered with defunt, old, broken, and janky throwaway bits everywhere - the documentation implicitly points out the core path of what's needed to understand how to bind and work with data in a web page.
Bravo!
I'm convinced this clarity comes from you distilling down what exactly _are_ the core pieces, and only implementing them first.
some rambling questions:
I wonder how this (synergyjs) works for a webapp type situation? Would one extract .js scripts as modules to reuse custom components? or is that trying to bring this into a more complex setup than is the sweet spot for this lib? e.g. is this better suited for a page at a time?
Also, maybe I'm dense, but does this approach lend itself to single page apps? My understanding is it shines when you have single pages with some logic behind them. If you need to jump to another page, the "passed data" would be encoded in the url, yes?
(I seem to recall the html 5 standards were looking to add something (was it 'proxy'?) that was something like frames, but involved web components, and maybe even passing of data through slots... is that a thing?)
I tried Preact a few weeks ago and the examples all worked, which was nice. And there's no build step -- you can develop with a CDN and hit F5. Big win IMO.
Edit: and synergy is also optionally avlbl via CDN for the no-tooling approach you liked in your preact experiments.
Same with React. There is a production CDN https://reactjs.org/docs/cdn-links.html
- it has a bunch of useful serialization sugar (e.g. `{{ classes }}` serializes correctly whether it's an array, an object of booleans, etc. This is similar to Angular (in React-land, this got pulled out into a separate package, classnames). There's similar sugar for `style` and objects (which React also supports).
- I'm not a fan of the variadic event handler signature. If I understand correctly, having a event handler inside a loop adds an extra argument to the handler function, but what happens if there are nested loops? In React-land, this would be done w/ arrow functions and closures; in Angular, one would write a js function call similar to plain HTML event handlers. I prefer the React approach (though I understand that isn't really an option for the way you approached synergyjs)
- Similarly, if `#` is bound to loop index, then how do you access outer loop index in a nested loop situation?
- Binding view property names to form controls of the same name is an interesting abstraction. React looks clunky in comparison (yes, even hooks). Angular has a similar facility, with the addition of allowing custom transformation logic (think getter/setters for bidirectional bindings).
- prerendering via a browser-like environment isn't the best approach IMHO (I've seen people run into issues w/ jsdom missing features, for example, and something like puppeteer is a bit of a heavy thing to be running frequently server-side)
- I like the clear separation between JS and CSS (but, then again, I'm an old school person). React people might dislike it if they're used to css-in-js per-component style encapsulation.
Overall impression: appears to pack quite a punch for 4kb (more so than Preact IMHO, because of how it handles forms). Docs are clear and to the point, but could use more examples/tutorials (especially lifecycle hooks). Would like to see it mature a bit (e.g. in terms of addressing things like nested loops, as mentioned above), and I think it would benefit from considering a less expensive SSR approach.
Very promising!
as.map(a =>
<tr>{
bs.map(b => <td onClick={() => foo(a, b)}></td>)
}</tr>
)
Notice how both `a` and `b` are passed into `foo`. There are other possible permutations, such as passing indices or properties of items. I believe it's possible to access `b.bar` easily enough, but it wasn't clear how to access `a` or the index of `as.map` from inside the inner loop.What I realized is, "who would add something unnecessary anyway?".
I mean developers would think of keeping it as thin as possible. Just that over the time, we run into senarios that need extra codes. Otherwise, users would think about alternative solution. And in web development, it very likely to happen
Hope that you work out the real competitive advantage
By the way, in terms of bundle sizes React is smaller and Preact is almost the same. But I think the reason is that react-dom is not included into calculation https://moiva.io/?compare=Synergy+preact+react+synergy+vue
When there are plenty of unknowns and you're working solo, you need a very thin layer that lets you craft a prototype quickly and not spend too much time configuring or generally investing a lot because it's something you plan to keep for a short time anyway, or it has a small scope that is never expected to grow. I think this framework kind of fits this model.
In a corporate environment, on the other hand, you're probably building something with the intention to last and then you need something that is established and has rigid interfaces, off-the-shelf components and Stack Overflow activity. Ideally, a framework that can only produce one style of code so that the next person or the next team can pick up.
Another possibility is you're on a budget, a handful, but know-their-way, developers and there is no plan to scale that for the foreseeable future. You want to try out ideas and be able to fail quickly. If you use React and TypeScript for your prototypes, you'll get a sound and solid codebase but also one that also cost you a lot of time. So, if just before the end and a couple of months in you discover you need to do some dramatic changes, you are left with little options and an empty budget.
Each needs to do their research and decide based on their unique requirements and more frameworks = more options, easier to find a fit. In my opinion, use the big frameworks only when you have a clear concept of what you're building. Otherwise, use something that gets the thing done as painlessly as possible.
Edit:
In addition to that, the world changes, and our concept of what is the proper way to build UIs or even what constitutes a good UI changes, as the world and the people change and new technologies emerge. If we aren't exploring ideas at the edge of our undestanding and knowledge, then we'd still be writing <font> tags in our Geocities pages.
Most front-end Javascript frameworks rely on either declarative widgets or some contrived implementation of HTML / CSS with a build step to get the developer's code to render in the browser.
Svelte's approach to sticking extremely close to traditional HTML / CSS & JS, and then compiling everything together, in my opinion, makes it much more approachable and maintainable.
very angular-ish, template heavy with it's own syntax.
I have no affiliation, just happened to notice it and really really like its philosophy, framework builder, readable code, and documentation.
I hope Andrew sees this comment so he can chime in.
Now I just need to find a project to use this on.
Are you using it in a project of yours?
And React is only about 5kB unless I'm mistaken?
FYI, I wrote something similar a couple of years ago but never took the time to publish a repo...
I like how your templating language is minimalistic (not embedding whole js syntax inside) and is valid HTML (could be prepared using HTML authoring tools).
I have few remarks comming from my expeirinece in wirting and using minimalistic templating engines.
--
This templates tightly integrate with html and magically interpret the view model values depending on the context they are used in html.
I think it could be better to explicitely indicate in place of use that the value will not be used directly.
Not
<div hidden="{{hide}}">
but <div :hidden="{{hide}}">
<div _hidden="{{hide}}">
<div :hidden="hide">
This would make it explicit, aria would not need to be an exception and HTML and your syntax would be clearly separated. You could just pass argument through funcion named `hidden` (or `class` or `style`) and even make whole thing pluggable with custom functions that preprocess the argument into the attribute. (Or even multiple attributes if necessary).---
Passing current item from `#each` loops into event handlers implicitly is very cool, but you might want to think about what if you have nested loops.
Maybe it would be cooler to pass not just the element but sort of `context` object that contains current elements of all nested loops and perhaps some other stuff in the future?
Also you might consider passing it not only into event handlers but all functions called from template.
Again you could use `:onclick` instead of `onclick` to indicate that there's some additional magic happening.
---
You might want to consider making loop variable optionally anonymous to reduce the noisiness of looping.
<!-- #each in people -->
<li>Hello {{ .name }}</li>
<!-- /each -->
---You could do form binding though `:name` to indicate that something beyond just usual HTML is happening and allow people to opt-out for some controls by using just `name` instead of `name`.
---
All of this could easily work with pre-rendering and hydration by not removing `:something` attributes when `something` function is called to do the appropriate magic.
<!-- #each row in rows -->
<tr>
<!-- #each field in row -->
<td>{{field}}<button onclick="edit">...</button></td>
<!-- /each -->
</tr>
<!-- /each -->
`edit` might need both row and field to perform the edit, and even possiby # of row and # of field.So a richer context passed to handler might be useful.
---
Regarding not calling functions from template by design it's all good, but you might consider introducing the ability to call some functions (through html attributes prefixed with : or _) during template interpolation.
Those could be plugins of your templating system that perform some operations (like generating attribute "hidden" or not depending on boolean argument, or binding form field to view model, or binding event handler, or something else).
Those functions might benefit from knowlege of template context they were called from (for example handler binders might want to pass this context along into bound handlers, like you already do with current item and your built-in handler binding solution).
I understand that this a bit different from what you made, but by extracting all of that functionality into separate self-contained functions exposed to the user, instead keeping it as integral, internal part of your template interpolation process, you could do yourself a favor. Because people who are missing some funtionality you haven't thought of yet might implement it themselves easily. This might increase usage of your framework and contributions from users.
---
Those are just some ideas where you might go further with your templating engine while keeping it simple. I hope I communicated them clearly enough, but if not and you are interested, please ask.
But claiming is as alternative is overblown, people choose those two frameworks because the rich ecosystem and support behind it, for which, except probably Angular, there are no way around them.