Web components will outlive JavaScript frameworks
jakelazaroff.com
jakelazaroff.com
first and foremost, the article doesn't talk about synchronizing state w/a server, which is what htmx is focuses on (via hypermedia exchanges), so htmx is orthogonal to WebComponents in this regard
> There’s a cost to using dependencies. New versions are released, APIs change, and it takes time and effort to make sure your own code remains compatible with them.
This is why htmx is dependency-free and focuses intensely on backwards compatibility (e.g. it is IE11 compatible). intercooler.js, which was htmx 1.0 and has one dependency, jQuery, was released in 2013 and is still supported. A lot of other javascript libraries have a culture of API rewrites and difficult upgrades, but htmx is explicitly not like that and shouldn't be heaped into that general culture. In htmx 2.0, our only breaking change is likely to be dropping IE support.
Will web components outlive htmx? Well, they are part of the browser, so presumably yes. But, insofar as I can help it, htmx should be very stable and churn free going forward. The API is basically correct (yes, w/ a few warts and mistakes) and I see no need to rewrite everything for the sake of purity or sexiness.
And, most importantly, htmx answers a different question than Web Components: how should I synchronize state w/ the server.
I don't think you need to change the article, I just want to give my perspective as the author of htmx. It isn't realistic to expect everyone to write perfectly precise articles at all times, especially when the htmx reference in the article isn't a focus (likely included just because you kinda like htmx :)
This is awesome to hear. I've been eyeing htmx a bit lately for some pet projects, but as a primarily backend-focused engineer, most JS libraries I import tend to be a large maintenance burden moving forward, with major API changes happening regularly.
I'll definitely take htmx out for a spin.
Historically I have constantly had an issue following JS frameworks and more importantly their build processes. Too much mental overhead when the majority of things I am building don't really benefit from a SPA style. I started my journey using Backbone.js a decade ago and enjoyed the relatively simple apis. Everything that came after for me has been a nightmare. Finally with HTMX I am in the groove and able to easily build what I want in a pattern that I enjoy.
The end result is that we can stick to Htmx's way of doing things, i.e. keeping all business logic state on the server, and only a few conservative things are client-side (such as currently focused element, text being typed in an input, things like that). Every step we take towards this, it offers a much better user experience with fewer bugs and race conditions, so it's worth the effort.
both are really for SSR applications, a modern jQuery to some extent. For CSR(client side rendering), you still have to use react/vue/mithril/etc
On a side note about htmx. Really compelling concept, I wish there was a larger community around it. Chicken and the egg kind of a problem, but there just aren't a lot of resources (blog posts, videos, etc) compared to many other web frameworks. Basic stuff like how do integrate something like Auth0 auth into an htmx app.
In a world of either SPAs or SSRs, htmx straddles the two. That is what makes it so interesting and unique, but it is also a different enough model to not always cleanly map from my pre-existing skills/patterns.
For the app I'm doing, I'd really prefer to keep it an SPA. In that the URL never changes, and there is never a full page reload (outside of OIDC/sign-in). I'm sure I can get there, I just need to think about the architecture a bit.
I'm kind of obsessive in this app about loading the app being nearly instant (I personally use it countless times a day). I really love the idea of returning back a full rendered page of HTML that wouldn't require any other API calls before showing the content.
The current stack (which I hate, but it works, I wrote it, and so who am I going to blame?) - is PHP/MySQL. My concept for something that will be lower maintenance going forward (keeping up on PHP versions...ugh.) is to use Deno + oak + SQLite (running in Deno using WASM) on the server to render HTML fragments/pages, and htmx on the front end. With no build step and no database server to maintain.
I think really all you need to do is start using it. The HTMX api is minimal but powerful. At the end of the day you are requesting chunks of html that replace existing sections in your html.
You are building an HTML driven app so your auth workflow is going to be whatever HTML can handle.
https://gist.github.com/1cg/d1ad1ddd5f43a8a993cd9f711135edc9
Here is an example button using these proposed extensions to HTML
<button href="/example"
method="patch"
target="#divToReplace"
onClick="this.submit()">
This is an example button hypermedia control
</button>https://htmx.org/attributes/hx-trigger/
w/ an explicit and declarative syntax, but that attribute is fairly involved
this was a brief exercise in thinking about the minimal changes necessary to HTML to support something like htmx, and unfortunately there isn't any attribute that maps to hx-trigger in the same way there is for other htmx attributes, so I resorted to the on* attributes + generalizing the submit() function on forms.
If everything and anything is clickable, how does technology that supports accessibility handle that? Note: This isn't a way-a-figure-you-fool question. Not at all. It's a sincere question in the sense that accessibility matters, we can't forget that. So if we change, how can we be certain accessibility isn't left behind?
To take the opposite view, there's not much we can't wrap in an <a> since HTML5 (eg <a href=""><div></div></a> is valid, and makes the div clickable). How do accessible browsers support that today?
It took a decade for JQuery to be nearly completely absorbed by browser standards.
Or am I incorrect?
HTML elements shouldn't do POSTs, that's why JavaScript exists. <form> and <a> are elements that do HTTP requests but the same can be added to any other html or custom element. html integration with JS doesn't need another layer.
State management is still unanswered. You'll end up having to do a lot of this yourself if you aren't using a wrapper framework. One nice thing about React is state management is mostly solved and there are existing patterns for it. There's expectations of how it's done. With web components it's the wild west, and since you can't pass complex objects as attributes like you can in jsx, you end up doing a mix of attributes, events, and calling class functions directly.
Performance isn't as big of a win as I'd have hoped. I actually reverted some smaller atomic elements (i.e. icons) away from being web components as their instantiation cost wasn't worth it when I had hundreds of these elements on the page. Static elements, despite the readability they provide in code (i.e. <nonio-icon glyph="hamburger">), just aren't worth using web components for, which was fairly disappointing.
That said, there are things I REALLY enjoy about web components. As the author shared, it's incredibly easy to add a component to new pages, regardless of the framework being used. If something is built using web components, you can truly use it anywhere. This has been really nice when building supporting sites like api docs for nonio, where I can easily pick and choose what components I want despite having a different stack. Need to embed a <nonio-user> component? Import the js, add the html, you're golden.
Style encapsulation of shadowroots is quite nice. I really love not having to worry about breaking styling of any component as I work on a page. It means less guesses and checks of clobbering styling of other elements, and less of a need for a visual diff framework to ensure this hasn't happened.
The other aspect I enjoy is how slim the package sizes are. No libraries or anything extra to load, everything is baked in. Because you can pick and choose components you need so easily as well, it's incredibly easy to keep package sizes down.
If anyone is curious, source components for nonio are here: https://github.com/jjcm/nonio-frontend/tree/master/component...
Also as for the state management side of things there is nothing at all stopping you from hooking up whatever state management solution you want. I’ve even seen a bunch of solutions that use the browsers built in event model as well if keeping dependencies to a minimum is your goal.
Finally, the last thing I would suggest is that writing an entire app in vanilla web components is kind of crazy talk in my opinion. For 5kb you can have a super nice developer experience using Lit (https://lit.dev)
I 100% agree with this. For me it was more of a question of "can I do it", and that was something I wanted to find out. You notice that I ended up having to recreate a significant chunk of lit-like functionality on my own via a base class: https://github.com/jjcm/nonio-frontend/blob/master/component...
I would very much recommend not going full vanilla. Using a library like lit will definitely help making things easier/more polished, and will integrate better with existing tooling.
Why?
I've done many.
My guiding principles:
- Avoid shadow dom unless it's a library. Stick with the light DOM and css system of your choice.
- Use lit-html or similar for rendering
- Use class properties for reactivity (add a render() call to setters, as needed)
- Ignore attributes entirely. Just use properties.
- Don't bother with composable components and slots. You generally don't need them
- Use a nice router like vaadin or ionic or similar to hoist components and support deep linking
- Let components maintain their own state. Structure the dom so that it's easy to grab state from a component with querySelector. If this doesn't work, I have a little state lib called ApplicationState[1] that works great.
- Really think about what needs to be a component vs normal html. Don't overdo it with components.
- Structure the app with high level "scene" components that map to URL deep links, and app components that you use to build the scenes. Scenes are targets of the router.
I've been doing this for years for many apps for many clients. Blazing fast, simple, no framework churn, no compilation, no framework download or overhead...
It's pretty tough to make myself use a framework at this point.
The entire reason I said that was because there is going to come a time after you have built probably your 5th component where you are going to start moving a whole bunch of shared logic into a base class that extends HTMLElement and that is the point you should probably stop and just bring in Lit at around 5kb total because they already did exactly what you’re doing and they almost certainly have done a better job with a bigger team and access to browser engineers with thousands of existing users and a bigger test suite with better documentation and well.. you get the point.
A lot of your argument seems to be quite focused on the idea that frameworks are some heavy weight thing which is perfectly true in many cases. But other options exist, and with a 5kb-10kb library download budget those arguments don’t carry a lot of weight.
I kind of thought this too at first, and started down that path several times. I actually wrote a whole framework similar to Lit early on, thinking the same thing.
What I've found over time, is 99% of that is YAGNI (You ain't gonna need it).
All my components just extend HTMLElement directly.
I explicitly dislike Lit because you lose control over the component lifecycle. I don't find it easier, I find it harder - especially when you're mixing in pure js libraries like draggable or whatever.
I prefer having to call render() explicitly, and controlling when/how that's done. At the end of the day, it's not actually any more code.
> a better job with a bigger team and access to browser engineers with thousands of existing users and a bigger test suite
Yeah, I don't need all that to show some interactive markup on the screen and organize code well. That's really what WC's are for me - just a convenient unit of encapsulation for vanilla JS/DOM stuff.
Lit is cool, so's stencil. I just don't need it.
lit-html, however, is probably my favorite piece of tech in the last 5 years. I've been using it since it was originally created and was only like 200 lines of code.
All that code I wrote is still working great, and hasn't needed any sort of update. On the other hand, if I'd gone with the Web Components Framework Of The Day, I'd have done Polymer and caused a massive headache for my clients.
Many people were making the exact same arguments you were about Polymer back then.
And then LitElement.
And then Lit.
Lit 2.0.
Now Lit 3.0.
Same story with <fill in your framework here>.
Angular 1.5 anyone?
Framework churn can kill a company.
I code to standards, keep it as vanilla as possible, and prefer libraries over frameworks (though sometimes the lines get blurry there - looking at you Ionic).
I’m quoting directly from here: https://lit.dev/docs/v3/releases/upgrade/
“For the *vast majority* of users there should be no required code changes to upgrade from Lit 2 to Lit 3. Most apps and libraries should be able to extend their npm version ranges to include both 2.x and 3.x, like "^2.7.0 || ^3.0.0".“
And here is v1 to v2 for completeness sake: https://lit.dev/docs/v2/releases/upgrade/
Most of your examples are kind of odd. One of them was a brand change that involves no code at all.
There is only one meaningful bit of churn in there which also was an overlay of the Web Platform itself.
Polymer was a WebComponentsv0 library
Lit is a WebComponentsv1 library.
I think most developers are very familiar with the framework churn that I'm referring to.
In my case, I've been dealing with it since the 90s.
So my biases are there for a reason.
What are the cases when this easy no code upgrade fails?
And yes, v2 update is quite a breaking change
> Polymer was a WebComponentsv0 library
It wasn't. It got upgraded to v1 almost immediately
Is performance a win at all? I would expect performance to be worse than using a regular web framework (React, Vue, Svelte, etc). Because these tend to get their performance from batching DOM manipulations across the entire page, and you can't do that will web components.
I figure web components are pretty much dead in the water for this reason alone. I guess they can be useful if you just want small "islands" of functionality rather a fully client-rendered page.
Also web components tend to use more modern layers of the web stack (style isolation, shadow DOM, etc) - which allow the browser to perform more optimizations, as well. I think we'll find that they become more performant over time. Also consider that some "native" html components are effectively implemented the same way as web components these days.
Often you'll find that React/etc are perf hits (vs finely tuned components) because their declarative nature tends to lead to unnecessary work …which is totally fine 99% of the time, because the productivity win from that is HUGE.
For state mgmt I have noticed the same problem and was thinking about trying to take a page out of desktop development’s playbook by having a window-level message bus that all components subscribe to and filter data from, and every state change being published on that message bus.
That and the the other big win comes from using native compiled C++ browser code to implement large chunks of what libraries typically provide in userland JS.
React is probably the worst offender here as it for all intents and purposes brings its own implementation of both DOM and events. It’s just an outdated architecture choice that made a huge amount of sense at the time but nobody would do that today if they were building a front end library from scratch.
Finally regarding your point about the message bus idea you might want to take a look at the BroadcastChannel API if you aren’t already familiar with it. Sounds like it might be what you’re looking for.
Really amazing. Thank you for this. Didn't know that.
OP above or anyone else can say anything about routing in context of web components? Deep linking? Any pointers?
Noob backend guy here.
When you bring in modern things like service workers and the prefetch API you can still very much keep the speed that is traditionally associated with SPAs.
Otherwise I’ve seen good things said about both of these options:
1. https://github.com/vaadin/router
2. https://github.com/lit/lit/tree/main/packages/labs/router
Both follow the mental model of mapping a URL pattern to a component fairly intuitively.
The big win here would be that if you have a huge app (let's say an ERP kind of app that has too many screens and dialogs and what not) then that can be easily decomposed into separate pages that are far more manageable.
You can easily wrap these pages up as Electron/Tauri app or Capacitor etc for mobile.
Deep linking however would be interesting in that if someone pastes a URL, we have to load the exact screen/dialog etc.
Not at all meaningfully different from say
void main(List<String> arguments) {
runApp(MyApp(arguments));
}
But you can!
Here's an example of passing complex objects as attributes (look for zx-listeditor): https://github.com/wisercoder/uibuilder/blob/master/WebCompo...
And here's the web component itself: https://github.com/wisercoder/uibuilder/blob/master/WebCompo...
And here's the tiny (500-line) lib that enables it: https://github.com/wisercoder/uibuilder/#jsx-for-web-compone...
You object to this tiny amount of code?
Or you just write a 'value' getter and setter and be done with it. I write most of my web components to operate like a form component. Forms have state. Forms have mechanisms for gathering state from all components and saving them into a single object. Forms have a means of restoring all that state.
You _barely_ need a layer of abstraction to handle all this. I've honestly never understood the "state management problem" or why anyone would think a framework is the way to solve it.
If the argument is that a site made with Web Components will outlast a site built with a JS framework then I'd be tempted to take that bet. I honestly think it's more likely that some part of the Web Components tech stack will be removed from Chrome, Safari, and Firefox in the next 20 years, consequently breaking apps built with them, than JavaScript will change in a way that means a React 16 app I wrote 2 years ago will break.
That already happened.
HTML Imports got deprecated (in favor of a JS-only solution), though Firefox already implemented them.
Custom Elements v0 was deprecated even though Chrome implemented it and hastily re-wrote Youtube with it. They had to wait ~4 years to remove the implementation because it took Youtube that much to switch.
Yes. What rarely happens though is that Youtube gets immediately rewritten with the new tech.
Hmm. I think you've specified a bet you are likely to win, but one that's nearly meaningless. Perhaps if you fully lock down all dependency versions your React 16 app will not break, but that's a little more like running an old program in a VM or an emulator than "I can still work and hack on my React 16 app."
A more meaningful bet would be, "can you still develop on a React ZZZ app vs. can you still develop on an app built with web components."
If I got to pick the terms of the bet (to favor my viewpoint XD but also to make it more meaningful), they'd be "would updating a React app to catch up on N years of dependency updates be more or less effort than updating a web-components built app to catch up on N years of dependency updates"; if you keep your web-components built app from having a bunch of weird dependencies, I think the odds are very much in favor of the React app taking more effort to update.
Basically, betting on browser vendors to be less likely to break backwards compatibility than the authors of React.
Versus if you use a library or platform (like Web Components) that cares a lot about backwards-compatibility, they will maintain the security while also supporting your old code for longer.
Put another way, this strategy puts you firmly in the “mid to late adopter” part of the adoption curve. Articles like this (and indeed much of this discussion) is largely targeting people who are earlier adopters.
You can also use MDX and achieve exactly the same thing. Web components the technology don't actually help here; what the author cares about is the interface between their markup and some piece of code that applies runtime functionality. Web components are an implementation detail here: whether you're referencing a React component or an Astro thing or a web component when you type a name between some angle brackets, it really doesn't matter: the point is that _some other thing_ is invoked.
The downside to web components here is that they can only be run on the client, for all intents and purposes. There's no way to pass server-rendered shadow dom content on the server to the client, so even if your web component is fully non-interactive, you still need JS on the client in order to render it. That's not the case with other frameworks.
I'll say that the author's point isn't wrong: web components as a way to codify interfaces between chunks of code is quite possibly the best, most durable way to accomplish that goal. BUT! On the server, web components do you no favors. They don't render, they are a cumbersome interface, and your code is already entirely written in a way that you can (hopefully!) work with. Which is to say, if you have Astro on the server, please keep using Astro! Or react, or vue, or whatever.
Where web components shine is on the client, when you have a component from a third party that might not be written in your framework of choice. You don't need to know about the framework the component you're putting on the page was built with. You just use a HTML tag and it works. The mechanism of the interface is uplifted to the browser level, so you don't need two frameworks to speak each others' implementation details.
> You can also use MDX and achieve exactly the same thing.
I would like to point out that plain HTML also has this property.
In any case, the point is that referencing another piece of code from your markup can be done in a number of ways, and the way the author chose has an upside but also a significant downside.
I thought you were going to argue that there is no way that a tag like <my-component></my-component> magically acquires all its inner html on the server all by itself. That it would need some kind of tool to help it. In that, I suppose, you are right. Although that tool can be literally any server-side templating engine that will fill <my-component> with the initial html it needs to bootstrap.
But server-rendered shadow dom can absolutely be passed to the client. It is called declarative shadow DOM; and it works in all major browsers other than Firefox; and Firefox is working on it right now. There's also a polyfill.
If you need to add a templating engine to generate your markup, you're really just replacing a framework with web components...and another framework. Which is half of my point: you aren't getting rid of a framework by choosing web components.
> it works in all major browsers other than Firefox; and Firefox is working on it right now. There's also a polyfill.
https://bugzilla.mozilla.org/show_bug.cgi?id=1712140
"Obviously not a commitment to implement, but potentially worth reevaluation" is not "working on it right now", unfortunately.
Here we are again, however, in that a completely non-interactive web component still needs JavaScript to render because you need a polyfill to take static, server-rendered HTML and put it in the shadow root. Again, it begs the question of what purpose web components are serving here anyway. The developer has to bend over backwards for both the server side and the client side and there's no meaningful reduction in code at all for either the client or the server.
There is a proposal for that, although nobody knows whether it will survive in the long run. https://developer.chrome.com/articles/declarative-shadow-dom...
BTW: the polyfill for this is rather easy, so you may be able to use it even browser today haven't support it now.
I'm still keen to learn more on the Inertia side of Laravel, but Livewire (and especially with Filament in the mix) just feels insanely productive as a Laravel dev.
If you manage to consolidate nearly all state to the server, complexity reduces by an entire dimension. There is nothing to synchronize anymore. You don't need these universes of delta-detection tools, frameworks, protocols, etc.
In most of our modern SSR apps, the only state the client needs to hold onto is a session token. If we find the client has a wrong session token, correcting this is quite simple. Everything else happens on the server.
Certainly there are holes with SSR (latency), but we haven't run into a real-world scenario where this actually matters enough to introduce entire new universes of hell into our lives.
What is the complexity you are talking about here, is this the human (programmers) understanding of the system or the design of a complex system itself ?
But wouldn't a solution that makes you more productive allow you to deliver faster?
That's what Livewire/Hotwire/etc are trying to achieve. Make developers more productive.
When going full on SPA there's so much complexity you need to solve. Crossing the client/server barrier with APIs, managing state in the client, etc.
It's funny how webcomponents alone are touted as an "alternative" to a framework, despite only being one component (no pun intended) of something that solves the problem of UI state-synchronization on the browser. A better comparison would be comparing a combination of custom elements, server-side rendered HTML, and proxy objects to some SSR framework like NextJS, Remix, etc.
The tide is slowly turning though with Next, SvelteKit, Remix, etc. But still these frameworks are basically just rendering HTML and going full on fat client which is overkill for 80% of interactivity in web app but adds a lot of unnecessary complexity.
It's exactly the same latency that a request to an API will have.
Obviously Livewire is not a good option for interactivity that happens exclusively in the client.
Every interaction in a livewire type system requires a round trip - that's not true for SPAs. Let's say I'm developing a node based editing UI with drag and drop that lets me connect things. How on earth are you going to make that work with livewire?
You’re wrong on this. You’d use Alpine and do the interactivity client side. If you don’t miss use the tool, Livewire is supposed to reach to the server in the same situations an SPA would reach to the server (updating or refreshing server state)
I've built some feature-rich FE apps that read and write to dbs & object stores with no servers needed! Very portable, the only install step is to upload a few files. They can be run right from a simple CDN. And they are super fast.
Most FE devs I know are actually familiar with Hotwire and friends but to them those stacks would add DX complexity. I think sometimes people say "complexity" when they just mean "unfamiliar". Both FE and BE can be way overly complex, but neither needs to be.
I do mean actual complexity.
With something like Hotwire/Livewire you don't need APIs anymore. You also can scrap client-side fetch() code and state management. There are some features that will still need some client JS but these will be a minority of use cases (if at all).
Yes, ultimately you need JS to interact with the DOM but if you look at eg Livewire the orchestration is in the server. I'd be happy to use a similar fullstack solution in JS/TS but afaik it doesn't exist. And it doesn't exist because the JS world is still hung up on full on fat reactive clients which are overkill for the immense majority of use cases.
If you mean, a fully TypeScript full stack solution then you might be correct. I wouldn't know because I tried Deno earlier this year and it wasn't great.
If you mean, a full stack solution that utilizes TS to for both the API and for the frontend then that is a much easier find. Vercel's Next.js provides this out-of-the-box and various other full stack starters and boilderplates exist.
I recently built one using PgTyped and typescript-rest.
And some being almost impossible to work correctly without MutationObserver or some polyfill of it. The renderProp in react (or scoped slot in vue) is so commonly used to inject data from the parent component into child component. But replicate such behavior in webcomponent is simply... impossible without MutationObserver hacks. The assumption of web component that child/parent component never need to know the info of each other just don't make sense in real world.
And the assumption that child component is always loaded and initiated regardless of parent component is also falsy in most frameworks. In most frameworks, the parent component decided whether child should be actually loaded or initiated.
Also, one reason I don't make web components even for isolated, reusable components: No editor properly picks them up for things like autocompletion of attributes and event names or checks for typos.
I worked in a shop that followed this philosophy. Every single UI component was a web component with its own npm package to install. It worked fine. There was zero benefit. There's still zero benefit today 6 years later. You're optimizing for a future which will most likely never come.
If you want to make sites and have a skillset that last, learn how to make progressively enhanced HTML websites. Anything like Web Components (where you invent useless non-defined HTML-elements-with-hyphens and then define them after executing a bunch of Javascript), Javascript frameworks (where you don't even pretend to involve HTML) will last only a handful of years.
(1*: at least until HTTP/1.1 support is removed from browsers)
It's a bit like "communism". You can say web components can be great or whatever, but everyone's every experience with it is very bad.
To address your point: the vast majority of web sites are text and images, not executable applications. Interactive tutorials are niche. Cool, but not something I'd base my entire web design ethos around.
[1] https://github.com/cheatcode/joystick
[2] https://github.com/mdn/web-components-examples/blob/main/edi...
The web component APIs are low-level and what you need to achieve interop with the browser. Most developers use something like https://Lit.dev
This is the go to "defense" I often hear from web component proponents.
If even MDN cannot come up with a better example, something's wrong with tech, not with how people use it.
> The web component APIs are low-level
It's not. It only got labeled low-level after the initial failure to attract any developer mindshare. And then it got rebranded as "aimed at library and framework developers". Though it barely got traction there either because it offers nothing lib and framework developers want.
> Most developers use something like https://Lit.dev
The irony of writing this under "Web Components will outlast your framework"...
It's not a "defense" it's a very wacky demo and I can't think of a single reason why you'd ever design a component this way. You could also design a React component or Angular component this way, and just like with web components, no one does, because it doesn't make sense.
I can't say why MDN decided to do this, but this is literally the only time I've ever seen a list represented as individual attributes.
> It's not. It only got labeled low-level after the initial failure to attract any developer mindshare
This... just isn't true. Web component APIs are things like customElements.define(), HTMLElement.attachShadow(), HTMLElement.connectedCallback(). These are objectively low-level compared to things that frameworks give you. Branding (whose?) has nothing to do with it.
Because it literally is the example on MDN?
> This... just isn't true. Web component APIs are things like
Yes, yes they define those APIs. They are both too verbose, badly designed and low-level to use them directly, and they are too high-level, badly designed and useless for most frameworks to use them as the foundation for anything.
However, even if we view them as "low-level compared to frameworks" they are still:
- extremely high-level (as all DOM APIs)
- were lauded as high-level and the end of all frameworks for years before being rebranded as "these are for framework authors akshually"
Or, to put it simply, they are as "low level" as React's class-based APIs of yesteryear.
Sources please? I've never met a single person that thought this.
> Because it literally is the example on MDN?
You're very stuck on this bad example. With even a cursory understanding of how Web Components work you can understand that you can recreate the same component accepting the same props in any popular component based framework. Or using Web Components you could do this example component just as many different ways as you could with another component framework.
There are many reasons to not use Web Components. Outside of some specific use cases I wouldn't use them. "A single MDN example weirdly used multiple attributes for list items, instead of child components or one attribute" is not one of them.
Web Components are 12 years old this year. They were lauded as the be all end all of web development to replace all frameworks for 4-5 of those years.
> You're very stuck on this bad example.
Because it literally is in one of the few authoritative places documenting web technologies.
It's not w3schools or some personal blog. I go to MDN to lookup or verify information on web technologies.
<editable-list
title="TODO"
list-item-0="First item on the list"
list-item-1="Second item on the list"
list-item-2="Third item on the list"
list-item-3="Fourth item on the list"
list-item-4="Fifth item on the list"
listItem="This will not appear"
add-item-text="Add new list item:"
>
</editable-list>
You could easily do this a different way, if you still wanted it to be an attribute <editable-list items='["first", "second"]'> and pass json to a single attribute. Or make child <list-item>'s.One thing about web components though, having shipped web components for an internal design system, is that a lot of people don't understand the difference between html attributes and the properties of the underlying javascript objects. In plain HTML, or most server rendered templates afaik, there's no way to pass non-string data declaratively like you could with say Vue where you can choose property vs attribute to bind data.
Yes, but that you have to make a choice means the design is poor. That's why stuff like React (imo) is a failure: there's a kajillion ways to do the same thing which end up creating dogma and sloppy/inconsistent code (read: bugs). Web Components—as far as I've seen—are making the same mistakes, but even worse, as a standard.
As a web standard, Web Components should have been more carefully crafted. There's zero reason the spec couldn't have allowed me to wrap a normal <li> tag in the example above. That it doesn't means, somewhere along the road, a compromise was made that shouldn't have been made.
The problem with all of that is when you introduce new web developers (who start by learning vanilla HTML, CSS, and JavaScript), as soon as they get to wanting to build more complex stuff (read: components), they essentially have to "unlearn" much of what they learned.
Why does that matter? Paraphrasing Uncle Bob: because the rate of new developers doubles roughly every five years, that means half the programmers in the world have < 5 years experience. If they're learning bad/inconsistent patterns from day one, that means the quality of the software they build (and God help us, the tools they create later) is on a permanent downward slope.
I don't know how you can prevent bad component designs like this.
const EditableList = ui.component({
render: ({ props }) => {
return `
<ul>
${each(Object.keys(props).filter((p) => p.startsWith('list-item-')), (p) =>
`<li>${props[p]}</li>`
)}
</ul>
`;
},
});
// Usage:
${component(EditableList, {
['list-item-0']: 'First item on the list',
['list-item-1']: 'Second item on the list',
['list-item-2']: 'Third item on the list',
['list-item-3']: 'Fourth item on the list',
['list-item-4']: 'Fifth item on the list',
})}
edit: used each(), which doesn't change the point that you can make a bad component API in any framework.There are many issues with Web Components, but I don't think this is one of them. The real pain doesn't start until you want to do something like:
<form>
<my-input></my-input>
<my-button></my-button>
</form>
and have everything behave as expected.You described another version of the problem I'm getting at. You should be able to use vanilla HTML, but you can't without hoop jumping.
It's pretty clear that you don't have a substantial amount of experience with Web Components, but have a strong opinion about them nonetheless.
And some super basic stuff like buttons in forms is still an unsolved problem:
> Form-associated custom elements: being a submit button
https://github.com/WICG/webcomponents/issues/814opened 4 years ago and representatives of major browsers still don't have an answer
Also you don’t need to pass objects as HTML attributes, I think people pick that up coming from React but the browser lets you pass around objects between components just fine by using properties
I wasn't able to find working example anywhere and all the discussions around the issue are still open which leads me to an assumption that this is in fact, still unsolved.
https://lit.dev/playground/#project=W3sibmFtZSI6InNpbXBsZS1n...
React is not an equivalent of Web Components, but neither it is a front-end Web framework: it is neither a framework, nor it is specifically for the Web. What it is is a fairly small and general reactive GUI library[0]. Together with ReactDOM—a separate library—it can be used on the Web, but React itself can be used to render anything anywhere if you provide a suitable reconciler.
As such, React (itself as old as Web Components) may well still be there whether or not the state of the art in front-end Web development moves on to whatever the next stack might be.
[0] The distinction between frameworks and libraries, which tends to be lost on many fresh developers as well, is out of scope here but vital when it comes to software architecture.
As for tiny, react-dom currently stands at 42kb minified gzipped. A tiny library would be about 1/10th the size (compare with preact or svelte).
Also, react has nasty rerendering habits, which makes if feel not so tiny at times.
But more importantly, have you read React docs recently? Have you seen how the official position of the core team is that if you are starting out with react, you should just pick a framework (Next or Remix)? Have you seen the extra complexities that these frameworks start to introduce (client-side state management in the age of server components, anyone)? React is no longer a tiny and very general reactive GUI library. For a tiny and general reactive GUI library, look at Lit. React, meanwhile, is attempting to become a kitchen-sink.
Remember: these are libraries. They compose. You can use one and not the other, e.g., if you are not rendering to DOM (and in fact even if you are rendering to DOM, but you come up with something smaller and more performant than ReactDOM that suits your requirements).
If you are saying “React is bigger than X”, you are comparing apples to oranges unless X can also do what React does. Which, per my previous comment, none of those frameworks can.
> more importantly, have you read React docs recently? Have you seen how the official position of the core team is that if you are starting out with react, you should just pick a framework (Next or Remix)?
It’s a combination of two factors:
1. Being a library, it’s by definition open to frameworks being built on top of it that facilitate library’s integration in specific solutions in specific ways. Without getting into the nitty-gritty of what it means to be a library or a framework, those frameworks add constraints on how the library is used, but they add value by providing scaffolding and streamlining some use cases. Therefore, it could sort of make sense to tell a novice front-end Web developer to just use a framework to get things done.
2. Unfortunately, React is increasingly being influenced by commercial platforms that use those React-based frameworks (e.g., Vercel). I believe some of the original React developers moved from Facebook to some of those companies. Those companies have vested interest in sending people their way, and I suppose they are or they feel entitled to use React’s reputation for that. I also think they are incentivised to not make React’s documentation particularly easy to use, to keep some features private while using it themselves (cf. RSC) for as long as they can get away with, etc.
If you look at my comment again, you may notice that I wrote "react-dom". I checked before writing.
> If you are saying “React is bigger than X”, you are comparing apples to oranges unless X can also do what React does. Which, per my previous comment, none of those frameworks can.
Does preact need a preact-dom library ten times its own size? Does lit? What is it that they do not do?
No, but neither does it work outside of DOM. As I wrote in my first comment, you can use React anywhere (native apps, embedded LCDs[0], etc.), as long as you provide a suitable reconciler. It’s a tiny general reactive library that is neither a framework, nor is geared towards DOM/Web in particular. As far as I know, the same cannot be said about Preact.
[0] https://github.com/doodlewind/react-ssd1306/blob/master/docs...
I know. This thread was specifically about the web though. Web components?
If you put React on the web, it is only fair to consider it together with react-dom in all the comparisons. Without a rendering library, React by itself, however small or generic, is not of much use. So what the absolute majority of people mean when they say "react" in the context of building things for the web, is the react/react-dom combination.
That is exactly the point. This author compares Web Components to React and Astro. Web Components is a spec. React is a general-purpose XML-based reactive GUI rendering library. Astro and Next are frameworks. Next uses React, and Astro can use React. I think you can use React and ReactDOM to render custom elements on the Web already (or will be able to soon), nothing fundamental prevents it. Either the author is not experienced enough to understand what is what, or he tries to gather engagement by inciting a holy war using false dichotomies.
> If you put React on the web, it is only fair to consider it together with react-dom in all the comparisons.
React-the-library doesn’t cease to exist if you happen to use it with ReactDOM. It also doesn’t automatically become a framework. In addition, the Web exists within the rest of reality. Even if all of the above contenders were also libraries, if one library works independently of particular medium (e.g., can be used in native applications, etc.) then it exists on another level than any Web-centric library.
> React is not an equivalent of Web Components
The only people who promote these comparisons and draw these equivalencies are the people who promote web components under the weird mantra of "use the platform".
In that context I 100% agree with him that if you want to sprinkle some interactivity while keeping your content "portable" and be able to move to the next "CMS" of the future, sticking with web components is a much wiser choice, rather than embracing whatever fancy convenient feature your tool provides (like Astro's islands).
(It is good advice and a well written article btw, thank you for sharing)
I don't think he was suggesting to build your next SPA entirely with web components..
To that end, the question will have to be if you can run the same web components of today on a browser of tomorrow. With the very notable fact that though you can't run components on browsers of yesterday, you can run that old jquery site on browsers of today.
Because React is a view library, not a framework.
Yes, web components might outlive React.
But no, they will not outlive my JavaScript framework, because web components don't replace routing, data layer, testing, authz, server-side rendering, rehydration and whatever else frameworks like Angular/Ember/Next.js/etc do these days.
Rendering a view is a small part of what front-end frameworks do.
This shows up with restrictions like that you can't mix and match components with different versions of React in the same tree.
Most people view libraries as tools which target narrowly scoped sub-problems of a given solution/application. In React's case, it's a view library -- the V in MVC, for instance. Frameworks are generally considered holistic set of tools which tightly integrate together to form a structured & opinionated approach to the larger solution, e.g the entire MVC approach.
React is widely considered a library. React doesn't tell you how to structure your project, manage its backend communication, handle application state, routing, etc. React is just...not a framework. For any standard notion of what engineers mean when using the term. If you use IoC to distinguish libs/frameworks, you're going to confuse the hell outta people. There are certainly technologies which straddle the line between these terms, but React is not one of them.
Technically it doesn't, but the conventions that grew around it and the availability of learning material effectively produced same default choices, making it a framework via convention.
And I see it as a good thing, because having several competing router implementations is an unnecessary headache.
The React crowd would do good to stop being in denial about this.
I think React doc, by calling Next a framework, is referring to a set of practices and protocols. But different usage of same nomenclature in the same discipline is very common.
Yes? If you create a React component, it does nothing. You define the lifecycle when you call ReactDOM. But you don't need to call ReactDOM. Call whatever you want.
When I use an external library, I link to static production scripts, without webpack or other bundler. I do use babel to handle jsx if needed, but I do it by linking babel via a script tag in dev mode (and with a simple shell script in production).
It’s more radical, not recommended for production use and impractical for anything other than a hobby project. But it’s a way to limit adding new dependencies, or limit reliance on package managers. Additionally, the project can work with as much as a static file server, also for development - so I won’t get stuck on an “npm update” when I return to a project after a break.
Most recent website I’ve built like this is: https://merely.xyz/lenses , list of Sony E Mount FF lenses.
Again, for most cases web components sound like a more practical approach, I plan to explore that long term too.
Looking for a way to learn enough to get something up and running. Do you know any source that explains using React this way? I've looked into the source of merely.xyz but I am looking for something simpler that holds your hand.
- custom elements - shadow DOM - html imports ( now dead or at the very least 'resting' ) - html templates
I think it's important to note that you can build/use custom elements ( defining your own tags with encapsulated behaviour ) without having to use shadow DOM nor html templates.
So a simple way to start, in my view, is plain custom elements, mixed in with ES modules and import maps.
Shadow DOM and html templates are not compulsory to get your <custom-tag></custom-tag>
i make my proper prostrations to the gods of the hot new thing
peering through reverent fingers I watch them flourish and fall,
and reliable silent old jquery, I notice, outlasts them all
With syntax so simple (and not a fork bomb),
No matter the changes in chorus and verse,
Every new framework . . . will make it all worse.
Within your questions and inner for loop,
Is readable syntax (not alphabet soup),
Give up on your promises, avoid callback hell,
Just keep using jQuery - I promise, it's swell!
> jQuery is used by 94.5% of all the websites whose JavaScript library we know. This is 77.5% of all websites.
This appears to be current data too (Oct 2023). I'm not sure how reliable the data is, but it is congruent with my experience that _a lot_ of websites still use jQuery. It's easy to forget the HN crowd is also in a bubble. If you only read articles here, you'd think everyone is using the latest JS framework du jour, but lots of the web still runs on PHP and jQuery.
Washboards might outlive washing machines but I still prefer the latter to do my laundry.
So working with them in real apps, you get a mess of opinions and assumptions like completely isolated styles (no I don't want this! let me use my site's Bootstrap theme!), etc.
Also dealing with string-based attributes/props is a pain. I greatly prefer React/Svelte/Vue for not having to deal with all this.
"Widget" that you provide for partners to put on their web pages? Of course you'll use shadow DOM, because you don't know the page context, surrounding styles, etc.
A component to share on different parts of your site - you can skip the shadow DOM, because you share common styles everywhere, and probably you want to inherit some of them.
A classic example of why this is beneficial is when creating design system components - you typically want these to not be affected by styling on the page, and if you do want them to be styled, you only want them to be styled in specific ways which you can build in via css custom property values (see the accent example the author provides).
Ironically, this proves my point about opinions a bit - our design system is themeable and needs to cover a number of technologies and we want to inherit the app's CSS, not embed the styles or have to specify colors on every component.
If you're curious on this approach, my site non.io has light/dark theming via this method, and Figma's light/dark mode was loosely based on this approach.
Webcomponents and React look like they solve the same problem but they do not.
Webcomponent api is pretty shallow. You get connected/disconnnected/attributeChanged call back but gotta write your own property setter and getters, slots, template, shadow dom and that’s mostly it. Shadow dom becomes a pain to work with if something needs to pierce it. No built-in webcomponent api for efficiently applying state changes to dom. All attributes have to be strings and other types need to be parsed.
Mixpanel went all in on webcomponents. They made their own framework on top of webcomponents. https://github.com/mixpanel/panel
Worked on panel lib and webcomponent UI for many years. Webcomponents are not a silver bullet.
The issue with webcomponents is there are a ton of libraries that fill in the missing gaps. There’s not a lot you can do with pure vanilla webcomponent api. Google has their own thing, Microsoft had multiple internal libs across orgs, Reddit does their own thing.
The most standard thing for frontend with wide adoption right now is React.
vanilla webcomponents are good for simple widgets in a vanilla html/css page. To build complex single page applications, would not recommend it. You’d be building you’re own bespoke framework to fill in the gaps.
Surely, yes. But I'd like to observe that a blog like this, one of the simplest web-based entities, could have remained implemented in some of the decades-old technologies and delivered the same experience to the end user.
They always sounded like a beautiful solution how to make all the various web frontend frameworks to live peacefully on the same page.
When you start adding them, you will be writing enough glue code that will offset a lot of benefits of web components.
The lifetime of apps is surprisingly short.
Newer technology on the web does break more often than old JS (maybe, Flash?), for example I have a project that for security reasons doesn't work anymore, Cookies famously also had a big change for security like that, fetch() defaults changed, and AFAIK even performance.now() was capped for Spectre.
(I work at Microsoft on it. You can verify with F12.)
Reddit doesn't seem to use web components at all? https://imgur.com/a/kzLoGnV
Youtube seems to use 1 web component for every 10 normal components, which is fair: https://imgur.com/a/YIBhLh8
Reddit doesn't use web component on all pages yet. They have them on certain types of comment pages now and are incrementally porting the rest of their site from React to web components with Lit. They've also built this year's r/place with Lit, and are building other things like embeds on web components.
This is exactly the point. Virtually no-one is using them (well, except for Microsoft, and Google, and Adobe, and Github, and Reddit, and NordHealth, and a bunch of others), whereas they are far more future-proof than any of the much more popular frameworks.
It will all change with time, of course. Just like there is no reason now to depend on jQuery. If only I could restart our app again, I would never have chosen React for it. And it worries me when I think how we are going to migrate off of it.
I wouldn't :-) Lit is just a tiny wrapper over native web components to add reactivity to their behaviour; but what is more important, Lit positions itself as a gradually disappearing library, intended to be replaced with native browser apis as they are introduced into the platform (e.g. DOM parts to help with templating, etc.). Compare this ethos to any other library out there.
Which spec is that?
https://www.w3.org/TR/html-imports/
The v0 spec is also vastly different than how it was implemented, though I don't think it ever shipped out from behind a flag:
https://web.archive.org/web/20130608123733/http://www.w3.org...
A technology that cannot submit forms without Javascript, still can't create a submit button and whose proponents agree that it still needs 20 more standards to be generally working [1] would be dead within a year. And yet we're here in year 12 of this dumpster fire.
[1] Instead of producing a report every year, Web Component Group seems to just update the same document in place, so now it inly lists "4 standards we agreed need to be done ASAP"
Lit [0] for example is doing this. It's a very small library, making use of the Web Components APIs. Using Lit is very similar to using React. Some things work different, and you have to get used to some web component specific things, but once you get it, I think it's way more pleasant to work with than React. It feels more natural, native, less framework-specific.
For state management, I created LitState [1], a tiny library (really only 258 lines), which integrates nicely with Lit, and which makes state management between multiple components very easy. It's much easier than the Redux/flux workflows found in React.
So my experience with this is that it's much nicer to work with, and that your total project size is much smaller.
[0] https://lit.dev/
They already do that wherever possible.
However, web componens provide almost exactly zero value to frameworks. You could ask a simple question: why did the authors of Vue, Svelte and Solid started as staunch supporters of web components (and even modeled early versions of their frameworks after them), and why are they now at best indifferent and at worst actively hostile towards web components?
> It feels more natural, native, less framework-specific.
If you ignore all the framework-specific things like custom template DSL, custom JS DSL, custom functionality like contexts and the new custom compiler, it's very native.
As an example you could take a look at an older project on github: https://github.com/cyco/WebFun/
Here is some example code:
https://github.com/cyco/WebFun/blob/ec42d59b99d62daafea3ccce...
https://github.com/cyco/WebFun/blob/ec42d59b99d62daafea3ccce...
enabling shadow DOM renders that component completely inaccessible to users using assistive tech like screen readers. without a shadow DOM enabled, you lose the encapsulation benefit devs seem to love. js framework components, for all their faults, can at least be accessible.
i just don't think web components are quite there yet.
That, and some progress on template instantiation, reactive DOM primitives or DOM partials, so we don’t have to concatenate strings and overwrite el.shadowRoot.innerHTML, or do direct DOM manipulation for any updates.
Until that all exists, and there’s a simple API to bring it all together, other frameworks will eat this space completely.
If so, then probably using svelte as a compiler for web components can be one way to do things.
Not sure about routing though. Maybe page.js or something.
Even for vanilla js, till might need vite, postcss, tailwind in some cases.
But when you start using Typescript or npm modules that export minified code, or there's a CDN involved, finding the original source for a function gets increasingly difficult, the type definitions are in a different file, and breakpoints don't work. It's almost like debugging a binary.
Compiled languages like Go do a better job of making sure you can actually read the source code, because imports always import source code.
Source maps with pretty great, and if you're in a development setup where you say only compile out types and resolve import specifiers, they work spectacularly well, including in debuggers.
According to this bug [1], even the typescript compiler’s npm doesn’t include source or sourcemaps.
No, this is not a joke.
Apparently based on https://en.wikipedia.org/wiki/Souvenir_(typeface)
See also https://fly.io/
All pointless frameworking is based on the fact that HTML doesn’t natively nest in other HTML or natively color in JavaScript editors. So we have ridiculous bloat, when that was all we had to fix: the ability to put HTML in JavaScript in a satisfyingly official way.
Instead, we have everything in fragments that have to be mechanically transformed, and fight over which giant IDE is best.