Web components are okay
nolanlawson.com
nolanlawson.com
The current state of Front-end frameworks is an absolute mess. Speaking for myself, I don't want to learn a complex framework. I don't want to learn magic that I don't understand without reading the documentation (useState, createSignal et al). Magic in frameworks is often a hack, unlike magic in libraries. The first library I used was Prototype. It felt like magic, and it truly was. And so was jQuery, and Backbone. I never had to guess what "useState" does behind the scenes.
There are many things which don't carry over into Web Components from current JS frameworks. But if you start from Web Components (ignoring everything you know about frameworks), it suddenly becomes intuitive. And it brings in abilities which are missing otherwise, such as isolation via Shadow DOM. It grows on you.
In my view the only thing we should retain from the React era is JSX (for many reasons, true type-safety, autocomplete etc). I wrote a library last week for using Web Components with JSX. No magic other than JSX. https://webjsx.org
Completely agree. As a casual programmer, I just want something simple, inspired in VB6.
How do you deal with the non-existing difference between attributes and properties in JSX? Is every attribute a property and vice versa? Do properties reflect back as attributes?
let element = <my-thing my-attribute={value} />
element.property = prop
Snabbdom [1] takes an approach of explicitly separating `props` and `attrs` at the expense of extra verbosity in the JSX `<span props={{ className: 'example' }} attrs={{ 'aria-label': 'Example'>Example</span>`. My Butterfloat [2] uses simple heuristics, starting with the props because you can conditionally type a ton of JS type data (and MDN link comments) out of TS DOM types for a strong autocomplete experience, adding common important-to-HTML copy/paste shortcuts such as `class` and `for` and switching to attributes if an XML namespace seems to be in use or the name includes a `-` and seems to be qebab-cased rather than camel-cased.
[1] https://github.com/snabbdom/snabbdom?tab=readme-ov-file#jsx
This can be taken over to JSX.
Only it's the other way around.
What we've lost is the ability to say "let's make a website today" and just start making UI without build tools or crazy install trees or massive libraries that need maintenance and security updates, and drop it on a server somewhere.
I keep thinking I can have that back... ES6 imports are fully supported now, modern CSS is amazing, all we really need is an SSR/a11y-friendly way to do some kind of nestable HTML macro/component, and we can party like it's 2007 (but with grid, and import statements, and all the other new shiny objects).
Even without that, a tiny system that just depends on tsc should be within reach at this point.
Honestly though, I just never really run into use cases where I need to "make a page in a day" and where modern tooling gets in the way. I'm comfortable with create-react-app, NextJS, Gatsby, and a couple other things, I can definitely put a new static asset project from one of those on a VPS by shuffling files over scp in the timespan it takes to make a pot of coffee, and I would strongly prefer having those tools than trying to be "pure" for hand-wavy reasons and unproven benefit.
You go party but for a large team (or group of teams) I will still prefer the explicitness of data flow and the type safety that Angular/React/Vue/… give me.
But if you just want a simple web page with some javascript or even a react spa, it's not a whole lot of buttonclicks. Super easy to get deployed on something like cloudflare pages too. IMO it's easier today than it was back in the html forms era.
Bear in mind that today's websites and web based desktop applications are much, much more complicated than two decades ago, and has a much higher requirement for scalability and maintainability. If you just want to create simple UI, indeed they may not be your best choice.
For example, you submit a form with a couple extra tags on a stock HTML form, do some processing server side and swap out the form (or redirect or whatever). No overriding the on submit callback, no having to wire up a validation hook, no having to think about where things are running.
React and angular have a very "let's solve this in JS" culture that creates ultimately unnecessary complexity demons for that 90%.
While this messy project likely isn't the best showcase of SvelteKit, I just haven't experienced its alleged simplicity: there are too many files with special roles, and too much magic transforming things behind the scenes. I'd take React over this any day, where the components form a simple tree, and the dependencies are (often painfully) explicit instead of being swept under the rug.
One possible advantage of something like HTMX + web components I guess is splitting things into a million files all over the place that can fetch data and manage their own state is painful which seems to be how most svelte/react/whatever disasters are made.
There's plenty of ways to make a HTMX disaster but the culture and methodologies being pushed are all leading towards simple, HTML first design which I believe might be the 'secret sauce' rather than the technology itself.
That is, the way I see it, a tree is a tree, whether it's superficially written in JSX or HTMX. But of the ways to move data up and down the tree (and/or modify the tree itself), I prefer the explicitness of pure React over the magic of many of its competitors.
It’s like they all read some shitty book similar to that one about OO patterns.
BTW, forms are broken in Web Components. From needing to wire them manually with Javascript via Form Data to custom submit buttons not working in forms (will be solved with another Javascript-only spec maybe in the next ten years)
I can't really parse this sentence. It seems to have a lot of ideas in it. Do you want to learn a framework at all, or have one that is so magical that it feels like it doesn't need to be learned? Do you want to read documentation or not? Do you think the problem with React is that its primitives are unsuited to the work you do in it? Or to the work anybody needs to do?
I've spent most of my professional career in Vue, so I don't have a great perspective on the React ecosystem. But Vue feels like it must be included in your "complex framework" statement. I've definitely felt some pain from overuse of the framework, or poor understanding of various features, libraries, etc. The ecosystem around full-on SPAs is indeed complex. I'm just not sure what other systems would let me personally, and us broadly, manage a complex stateful client-side app.
(This is entirely separate to overuse of SPAs for things that shouldn't be SPAs.)
He referenced Backbone as a comparison point. If you compare Backbone’s source code to React’s source code, you’ll get what he means.
Basically, libraries like Backbone are small and simple enough that you can literally read the source code and fully understand how it works. Compare that to React, where the source code is an order of magnitude larger and very difficult to fully grasp the inner workings.
The simplicity and ability to easily understand the source code obviously comes with tradeoffs (e.g. with Backbone, to use it to build a reasonably complex app, you basically have to build your own framework on top of it, compared to React which has more abstractions and therefore is more plug and play.
I haven't bothered fully grokking it because I've never had to understand the impl details. Solid gives me literal dom elements, and that's low-level enough for me.
Did you know React renders twice in developer mode to try to expose bugs with side effects?
If this doesn't raise an eyebrow, I don't know what will.
To me magic code is code that requires a significant level of cognitive investment to understand and keep in your brain RAM. That threshold varies between people but it's always there.
In React's case, the chosen solution is not the issue here. It's a symptom of bad architecture. The library should be doing more than rendering twice to protect developers from side effects, to a point that rendering twice wouldn't be required.
That feels as bad as rebooting JBOSS servers once a day to "fix" OOM crashes. It's a hack at best and doesn't fix the real issue.
Like what?
I suppose I would think not rendering twice and some sort of warning about side effects would be beneficial, rendering twice means that you get a misleading view of what your application behavior is, I can see how people would - from an architectural perspective - consider it bad without necessarily using the framework.
Rendering twice is exactly what allows you to detect that. How would you detect a side effect if not by calling the function twice?
React's contract is that your component should be allowed to render twice without any changes so this is just upholding the invariant in dev mode.
Learn Elm and you'll see React's shortcomings clearer. They don't render twice but that is just the tip of the iceberg.
Be warned though, exiting the cave is painful.
However, how can react know what a side effect is? Hook into well known ones by hijacking the browser API? That sounds like a bad idea. Provide their own linter/compiler. That seems on the radar but I'm not sure I like the idea.
Also react doesn't stop you from doing it. You can say nah it's fine I want my side effect. It just runs the render explicitly twice in development, something that could happen in production for various reasons, so you can see what will happen.
Your proposed solution isn't very practical. A better solution would be a guarantee that render will only run once. But that's not how react has ever worked. There are other frameworks that do that. As with anything, there'll be a trade-off.
That's not a contradiction to it being bad magic.
In fact, this being a pragmatic solution is a strong, though not conclusive, indicator that the underlying abstractions are not fit for purpose.
And the fact that lots of pragmatic choices led to those abstractions that are not fit for purpose also is an indicator that something about the starting point or the road is not right. Or both.
You say that, but react is very successful, indicating that it's abstractions are working just fine for many people. Maybe we'd say they're not perfect but they fit it's purpose. It might not be my favourite frontend library to work with, but it's the one I'd be most likely to choose for commercial work because it does work well, and high use means that people have explored many solutions to problems with it.
React is an implementation, not the underlying abstraction. With react it's not even clear what the underlying abstraction is.
My view is that the problem domain is not complex enough to warrant the complexity of our "solutions".
See also: https://www.youtube.com/watch?v=ubaX1Smg6pY
Is it really "Complex"? Or did we just make it "Complicated"?
If I look at your library, it seems to me like it requires a much more complex mental model to begin to use.
Of course, it is better in theory for a developer to thoroughly understand the details of their framework, but empirically, React has been very successful in allowing people to build relatively sophisticated applications while understanding very little of the underlying model.
As soon as you work on any remotely complex codebase, you will run into problems that require a decent mental model of the underlying "magic" to properly understand and solve. "Building sophisticated applications while understanding very little of the underlying model" is how you end up with gigantic piles of unmaintainable spaghetti code full of awful hacks, which seems to be the standard for React applications.
I've worked with a lot of different tech stacks over my career and every single one of them has required understanding the internals once you start using them seriously. I haven't found React to be substantially worse for that than any other tech stack I've used.
Which isn’t to say it can’t be worth it. Learning multiple frameworks and libraries is also very helpful to skill up because you are learning about different concepts and implementations.
I feel like you eventually do. The issue with React, at least in my experience, is that it's a type of abstraction that seems ill-suited for how the web works under the hood, so it's incredibly leaky. Everything seems to make sense initially, and you get along just fine, but then you run into an edge case, and there's an official workaround for the edge case, but then you run into edge cases for the workaround for the edge case, and suddenly, that's your whole life.
Before you know it, you really do have to know how things actually work under the hood.
I feel like it's just a natural law of any general purpose framework.
It made the first 80% of the job easy. Now you just have to finish the other 80%.
FRP is a nice fit for the kind of UI that makes the majority of the web.
Hooks ruined the framework imo. Confusing api (useState returning an array for example).
Having a class component with hooks for lifecycle behavior made perfect sense. Each component’s state being a field on those classes was easy to understand.
Hooks came out of left field and made everything more complex for the worse.
It just returns a tuple. That's not confusing at all and it's a very well established pattern in most modern languages.
If there's anything that can be considered complex and footgun-y in React it's useEffect because most of the time you shouldn't use it at all but it can be abused very easily and it kinda works even if you abuse it (but introduces a huge maintenance burden).
A few things wrong with this. It does not return a tuple, it returns an array with 2 elements. You, as the react dev, need to just know that this is the case and that one is the value and the other is a set function.
In most modern languages, tuples are common, yes. But not in JavaScript. React isn’t a python or go framework, it’s a JavaScript framework. Why doesn’t it act like almost everything else in js land and return an object with 2 named fields? Why place extra knowledge burden on the developer? It’s just poor api design
Yes useEffect is also a huge complex pain point, but it just takes longer to type out why that is.
Pretty much all the hooks are as almost nothing else in the JavaScript ecosystem acts like they do.
The explanation is actually very simple: because in 100% of times you need to give those two fields custom names. It is easier and more concise with tuple:
const [text, setText] = ...
Than with object + renaming, which quickly gets clumsy when you have dozens of such lines in your component: const {value: text, setValue: setText} = ...Especially in our world where strong typing is something preferable.
Fewer characters at the cost of clarity is poor api design. Implicitness is almost always worse than explicitness.
Explicit !== clumsy
And again, almost nothing else in the js ecosystem behaves like this. Especially at the time of the big hooks update to react.
Also, again, it’s not a tuple. It’s an array with 2 elements. JavaScript does not have tuples or the semantics for interacting with them as such. You just need to know that there’s always going to be 2 elements.
That extra knowledge burden is the hallmark of poor api design.
Not true. In TypeScript, there are tuples — you can perfectly describe an array having 2 elements with different types, and even give names to them:
type KVPairTuple = [key: string, value: number]
We use it all the time for simple 2-element thingies. You can say JavaScript isn't TypeScript, but cmon, just everybody out there uses TypeScript, it is the basic norm nowadays. So we're really talking about TypeScript semantics here.Also, even JS uses tuples for its core APIs. Enter `Object.entries` and `Object.fromEntries` — they operate on kv-pairs in form of 2-element arrays. So your assertion that tuples are something alien to JS ecosystem isn't accurate simply because of that.
Not talking about typescript.
Typescript wasn’t nearly as wide spread or as complete back then anyway. Facebook was still on flow.
> they operate on kv-pairs in form of 2-element arrays.
So… Not tuples…
> So your assertion that tuples are something alien to JS ecosystem isn't accurate simply because of that.
My words were “almost nothing else.” I’m sure someone somewhere uses the idea of tuples in their js code (implemented as arrays, ofc) but it’s extremely far from the norm.
Also none of that refutes my point that it’s poor api design. Thanks for being pedantic tho.
TS is just encoding JS semantics into a type system.
A function returning a fixed-size array in JS is effectively returning, semantically, a tuple. They're just not a different type unlike in other languages (for different reasons).
JS arrays are not even arrays since they (1) are not contiguous and (2) do not require items to be of the same size (typed arrays are actual arrays though) except by their backing implementation (which uses js vals, but that's a property of the implementation, not the language). They're sparse vectors. But nobody cares about that technical distinction and we still call fixed-size arrays tuples and variable-size arrays arrays. You can even attach properties to them because arrays are not vectors, they are objects. Or can be, for that matter. The backing implementations are complex and very dynamic.
JS was intentionally made this simple so it could have emergent semantics. Scheme-ish.
> Typescript wasn’t nearly as wide spread or as complete back then anyway. Facebook was still on flow.
It was when they introduced hooks.
Typescript is irrelevant.
The implementation of arrays in js is irrelevant.
Js being intentionally simple is irrelevant (and might even strengthen my point)
One thing is some random payment processor API that you encounter only once in your career (it doesn't need to be terse and it is better to avoid any magic here) — and whole another thing is stuff like jQuery or React, which I can produce/read in thousands of lines per day. You would design such APIs with different objectives, like being easy to type, easy to read (once you know the API).
The mantra "explicit is better than implicit" is wrong, because it really depends.
We have a rule of thumb in our projects: if you use useEffect directly in application code, it is a probable sign that something is wrong with your code, like you're working on a wrong level of abstraction. Almost every direct use of useEffect is better solved with some standard hook from a popular library (like react-use or whatever you like — there are plenty of them).
Like, binding event listeners? useEventListener
Network/async calls? useAsync
timeouts/intervals? useInterval, useTimeout
And etc. etc.
useEffect is really a low-level building block for library code and we rarely use it directly, as it is unsafe and hides the original intent. Much like `new` and `delete` in C++ — you don't use it directly, there are smart pointers. Library hooks are your safe smart pointers over useEffect.
Instead it got a complete paradigm shift to a leaky abstraction. Not that the previous abstraction wasn't leaky, it was alwayz super easy to have components re-render when not necessarily.
It would bear almost no resemblance to the web we have today, but message passing between UI components throughout various parts of their lifecycle has been a great way to model an interface for decades. You only need to look at the overall fit and finish of a typical MacOS or iOS app, with Swift simply pulling Objective-C into the 21st century.
The major difference between web UIs and desktop apps is two fold. In web UIs, you don’t generally have an event loop in which you change your app state based on system input events and there is, generally, a slow request/response cycle for any interaction.
That’s why immediate mode UI doesn’t really fit well in web land.
How so?
It has two functions:
(1) createElement(jsx): Allows you to use JSX to write HTML markup. Returns Virtual Nodes.
(2) applyDiff(parent, vNodes): Merges Virtual Nodes created with JSX into the real DOM efficiently.
This is all you need to know. I can keep it simple because I am not doing much in the library. I felt that if I stayed close to the standards, I wouldn't need to do much.
I'm more of a backend/embedded developer than a web developer and I still honestly don't get how people find useState/useEffect as intimidating as your comment makes them out to be.
People tolerate this because they learned it but I don't think there is anything essentially simple about it.
The standard hooks delegate to a dispatcher, which has access to the current fiber. The current fiber has a linked list of memoizedState for the current work node. It's true that a lot of the functions that eventually service the hook calls do contain closures. But that doesn't seem to grant much insight into how to use them or how they work.
It's like saying "if you have an understanding of loops, the reconciler is quite intuitive". I mean yes, the reconciler uses loops. But the behavior of reconciliation may still be quite mysterious.
I think of React components as not closures but coroutines (and hooks are its yield points).
Hooks are implicitly keyed by index which is the most magic-y/surprising part... but I'm sure if you had to manually key them that'd be criticized too (and would be abused to death-by-bugs, so I can see why they went with this design).
If you understand both points above, you understand hooks.
I still fail to get the (usual) criticism of hooks. They have lots of warts but the API (which is often what gets mentioned) is the most superficial and less annoying criticism. Feels like something that would be mentioned after just skimming the docs. Very shallow.
On the contrary: useEffect is a footgun and deserves criticism. useRef being overloaded to mimic instance variables is confusing for newbies (but this is just a naming issue IMO). The difference between normal/layout/insertion effects is complex and subtle. The new stuff that tries to solve some issues with the concurrent mode (like transitions/deferred value/etc.) feels like a huge hack.
React 19 will come with its own warts (actions, RSC...)
But the API? I don't care at all. Pretty simple, at least for my mental model.
Hook's API is not perfect but it's a good-enough abstraction that allows the user to have even better abstractions and separation of concerns.
Actual React users did not care about that because the pragmatism far outweighs the theoretical ugliness... which honestly is not even that ugly if you have a mental model similar to coroutines (of course if all you do is OOP a class will look better to you...)
I have recently been fixing some stuff in my old React pre-hooks code and I hated it because class-based components had all sorts of concerns intermixed on their lifecycle methods... no matter how much you tried to abstract them.
Abstracting those into reusable hooks was a breeze and made everything much easier to follow and maintain.
Hooks are far better from a pragmatic point of view.
> There are a handful of things that were really hard to do with class components that hooks + function components made easier, but lots of other things became more complicated
Like what? Does not match my experience at all.
Funny thing is, I do not know what coroutines are and have zero experience with them. I am object oriented class programmer who never cared about functional programming.
I figured hooks intuitively and straightforwardly when I had to work with react a year ago. Ok, function with state, but it was easy to read them, imagine what they do and maintain the code. It just seemed as an improvement over the old react to me, despite having no coroutines knowledge.
To be honest they're not full-blown coroutines (since the React runtime cannot control when the component progresses) which might be a bit more complex to grasp... but the idea of "yielding control" to someone else (i.e. to React, when calling one of its hooks) is there and as you say it's pretty straightforward.
I think this just means your components are too large and try to do too many things. When your components are simple, their concerns are simple enough that they can immediately be understood, even if spread over several lifecycle methods.
However, the same problem started afflicting class-based components prior to the introduction of hooks. When fibers were created, component instances no longer tracked their own state. State was injected into them prior to invoking the render function, based on reconciliation. I suppose even before that the reconciler was still choosing which component instance to render.
But at least components had an identity that could be addressed in application code. Now component identity is effectively the fiber instance identity (or its alternate), which is impossible to get a reference to.
In real life, people really seem to like hooks. I can see some of the benefits they provide over the OOP class-based component model. But I can't avoid also seeing the mental foot-guns associated with it.
The very first time I every tried to write a react app, I got tripped up by this. I add a component that moves around in a parent component conditionally. So I had `const inner = <Inner />`, and then based on some condition it would be inserted in various places in the rendered parent component.
Of course after much wailing and gnashing of teeth, I learned that this variable declaration wasn't establishing a component identity. It merely creates an "element", even if it has a key attribute. Component identity, as used to retrieve state, is fundamentally tangled up with reconciliation which can only see the final rendered output of a component. No other components can have state, at least as recorded by a hook.
Most people don't seem to have trouble with this, but it's not how I naturally think of things.
Identity in React has always relied on vDOM structure or `key` which was introduced in React 0.4.0 (July 17 2013, <2 months after initial public release).
> For me, it's not OOP vs functional.
Neither is for me. Note I didn't mention functional once.
You are correct. But so, I believe, was I. Apologies for being unclear. vDOM structure is determined based on the return values from render functions. As opposed to the line of code where elements are created. So if you have `const cmp = <Comp />` in a render function, it doesn't have an identity yet. As you note, that will be determined later, sometime after this render function returns. It might not have a state at all. It might even have multiple states. If the current rendering is an update, `cmp` might be a mount instead.
All these determinations are made based on the content (structure and key attributes) of the assembled vDOM and the reconciliation heuristics.
> Note I didn't mention functional once.
I assumed that's the comparison you were drawing with this line. "better" than what?
> of course if all you do is OOP a class will look better to you
The lifecycle concerns are still there, sometimes things need to happen when a component is first displayed, and sometimes things need to happen with a component is removed from the page. It is just handled differently now.
My other issue with hooks vs classes is that, and I say this as someone who loves FP, the best application of OO programming is for UIs. At the base level, a text input field has an object created in the browser, and that input field has state. UIs are inherently stateful things. The OO model is natural for building UIs, you typically shove an object on the screen, that object has some state, and the user manipulates that state.
OO is a great abstraction for UIs. IMHO it is a pretty bad abstraction for most other problem domains, but for stateful UIs, OO maps pretty darn well to what is actually happening!
I just don't see the benefit of throwing another abstraction layer on top of all that.
One reason I love Svelte 4 (haven't tried 5 yet) is that it is so bloody simple compared to React. After years of programming in React, I was literally 5x more productive in my first ever Svelte project than I had ever been in React.
All I need is a good way to encapsulate HTML components for reuse (which is fundamentally an OO thing, instantiate new instances of a component template!) and a good state management system that pushes state changes out to subscribed components.
For me, this is the root of the problem. Or one of them anyway.
Hooks are keyed by execution order, but also the reconciler's opinion about component identity, which you can only control indirectly. Fortunately it usually does what you want. Unfortunately it's not all the time.
I feel like I do understand hooks, but it's not from reading the docs, and it's not from using them. I know this is not typical but doing those things didn't seem to illuminate much to me. I would still find them doing things I didn't want that I couldn't explain. It was only after reading the source that I feel like I understand the model. And personally, it's not one I would use by choice. I can do it if I'm required by a team I'm on. But for me, there's a mental overhead for "thinking in react". It's not a natural set of constraints for me.
When a function component is called it is called on a "fiber" a stateful representation of the component instance.
This fiber is available to the hook as if it was global variable that is set before the component is rendered.
This is why you cannot call a hook is a setTimeout or a callback to another component: the global fiber is either unset or has a different value.
The other part is that each hook invocation works on a state accessed as sorta fiber.hooks[index++].
So for example you can call hooks in a loop or in conditionals or in synchronous callbacks, but each rendering must be compatible with the first.
Eg
let s;
If(Math.random()<0.5) s=useState({});
else s=useState([])
Should work.You could also do the same with useEffect if you keep the length/nullness of the dept array.
The key aspect of hooks is that what you are doing is something like this:
function(context) { context.useState() }
Where context is a variable that stores all the hook related data, except in reality that variable is a hidden global variable and useState() accesses that variable internally.
Yeah, they added hooks as global functions so that they look like a "cute" DSL. It saves you the effort to type c.useState instead of useState I guess.
The above code is just for illustrative purposes to get the idea across, according to other commenters the internal implementation has changed from what I remember, but the principle is still the same. Global functions demand global state.
If you don't know what triggers a re-render you can end up in a state where the data was changed but the UI didn't update, or where the data changed once and re-rendered multiple times.
In my experience the issues are more noticeable with async state changes, like making network requests or state-driven animations.
I just haven't seen any benefit whatsoever once you have CSS modules. And CSS layers help too.
We went for a web component because we needed the same functionality on different websites using different tools.
Issue was that we authored the web component with tailwind and then deployed the web component on different websites, some having different versions of tailwind themselves. Even minor differences in hosts tailwind versions led to quite surprising results.
What we did in the end is to write a tool that given the tailwind classes used in the webcomponent it would then rewrite the rules at the :root of the web component's own css.
Another reason for unhappiness came from lack of isolation regarding the units that derive from the host's base font which forced us to work in pixels directly.
All in all, while I understand some of the decisions that went into web components, I can't lie and say that I too have few reasons to not be fully on the web components train, they fall short of the promise of author once, use everywhere.
It's really not that hard. I was able to pick up Angular to a level where I could create complex sites with it, or submit acceptable PRs that involved in, in week or so.
Granted, I am an experienced developer (C# since beta, HTML/JS/CSS from the start) but it seemed to just make sense quickly.
So when I was asked by a company to replace their aging, complex portal to manage critical infrastructure, I chose Angular for the front end.
That was smooth sailing. It was stable, performant, looked good and everyone was happy.
People often avoid it because it has a "steep learning curve" but that mainly seems to come from developers who came into the industry knowing only JavaScript and maybe a little React. If you are more experienced, you can pick it up right away.
And it helps, a lot, to use such frameworks on large complex sites. For various reasons.
Frameworks in C# are slightly better (compared to those on JS), because there are blessed Frameworks and most people are using them. In the JS world, you can see very popular frameworks (with millions of downloads) get abandoned. Not saying that all is well in the .Net world; desktop frameworks are an example. But overall it's a bit different because one company more or less controls it.
We all just programmed things for decades when web guys pop out with this “state management” pseudo-problem and started to invent thousands of pseudo-solutions for it.
And why do we need “optimization”? It’s a user interface, not an accounting software core. A user types a character, you update few divs content or way under a thousand of divs’ visibility. Which optimization does that need?
All real world programs are stateful. You need some tooling and/or conventions to handle that in a clean way. I recommend this blog post about React Query that talks about some challenges and solutions on the frontend for state management: https://ui.dev/why-react-query
You're questioning the importance of optimization on a website for hackers/tinkerers/engineers?
You need optimization for page speed, SEO, animation, elegance, pushing the boundaries...
edit to address: "Which optimization does that need?"
Optimistic updates, debouncing, handling large responses, cache, etc. etc.
If you don't think frontend engineering solves serious problems, I don't think your opinions about it are serious.
I don’t think “frontend” is at all serious, because I have developed user-facing systems since around 2000, and “frontend” (which we called gui controls back in the day) was never a biggest story, when it was worth telling at all. Mostly complex distributed accounting software with various integrations at all levels. I’m not afraid of appeals to serious engineers with serious problems, cause I am one of them and can question it freely.
If I appear somewhat toxic, that simply reflects the toxic positivity about the absurd state of things in web dev. It is a collective delusion which suddenly disappears when people get forced out if it, by e.g. going htmx route. Not advocating for htmx specifically, it just happens to be a perfect litmus test. Same for php, people still use it outside of “frontend” bubble and don’t know that “managing” their “state” is a hard problem that requires tens of versions of the best framework of the month.
It's largely unnecessary there because those can talk directly to the database and use it as their data store without a large performance penalty. Plenty of server-based systems like PHP don't even have persistent state to manage, whatever wasn't stored in the database is discarded when the response is sent.
> It is a collective delusion which suddenly disappears when people get forced out if it, by e.g. going htmx route.
There is a performance penalty here where every state change is a web request. That's a large part of where frontend state management libraries began, with Backbone providing objects that would be synced with database tables and acted like a local cache.
There is a performance penalty here where every state change is a web request. That's a large part of where frontend state management libraries began
That mode of operation is actually anti-frontend. Frontend (thick client) means that you have a local context with manual or eventual syncronization (aka [auto]save and [auto]update). Using these modern “state management” frameworks this way is double fault, cause you basically simulate 1.0 in 2.0 via now mostly useless approach that didn’t make much sense to begin with.
Request penalty is not a new thing. 20 years ago LAN servers had connection properties similar to the modern internet. Somehow (and I say this ironically, it’s very straightforward “how”) db servers, app servers, thin and thick clients and bare apps worked without “managing state” via some semi-functional but not really DSL language wrapping anominations. Only web, which was always a second class dev env invented the problem from its own poor childhood: jquery et al, no concept of permanent storage, poor io-unaware language until around 2015.
The whole point of web components is the ability to offer class encapsulation within the browser without anything other than a file server.
JSX has much nicer syntax but I’d rather not have to deal with the overhead of launching a node server. Makes developing time quicker when working with smaller codebases.
It's weird to me that React hooks are always dragged out in these arguments as some kind of bogeyman. If you understand the idea of a virtual DOM and a render loop, then it is only a tiny step from there to understand hooks. And you can understand all of these concepts in about 15 minutes: https://www.youtube.com/watch?v=1VVfMVQabx0
I just don't get all the hand wringing regarding frontend frameworks. I've been using React since 2014 and in 10 years, there has been exactly one big change to the framework: hooks. When they came out, I spent 15 minutes to understand them. It didn't kill me.
And React has been undisputed king the frontend frameworks hill for at least 9 years, but people still act like it's some sort of ever-changing, confusing landscape of options. If you want boring, stable front-end development, choose a super popular, well documented tool like React. Or if you don't like frameworks, use vanilla JS. It's not a crisis.
Once you understand the arcana of the magisterium, it becomes obvious why the encyclicals must be unitarian!
honestly lit-html is much better and based on existing browser APIs. We should make it part of the webcomponents spec! Which is a work in progress:
Ah yes. A new framework with its own custom DSL, its own custom hook, sorry, "directive" rules, it's own incompatible flavor of SSR is definitely the way to go, and whould be a part of the standard unlike Polymer before it which was also touted as the bee's knees.
> We should make it part of the webcomponents spec! Which is a work in progress:
Whatever the outcome of that discussion, it won't be lit.
I'm always suprised reading statements like that. No offence, but maybe you're in wrong career if you don't want to learn how most popular and important tools of today work... Not to mention that its so simple you can learn everything required to be efficient during lunch break
This is such a common comment that I am never surprised to see it pop up whenever the "front-end mess" discussion arises.
No, you cannot learn React over a lunch break. You cannot learn Angular and Vue over a lunch break.
I am skeptical that even you, personally, can learn just the footguns alone present in the hundreds of thousands of lines of code in a specific $FRAMEWORK over one lunch break, two lunch breaks or even a dozen lunch breaks.
All those thousands of lines of code implement something, and I will wager a month of my income against a month of your income that you will not learn more than 3 or 4 concepts over a single lunch break, because they all have multiple concepts, multiple use-cases, multiple exceptions-to-the-general-rule and multiple footguns without even including the edge-cases they solve.
You will spend several years of lunch breaks, unless you consider reading the wikipedia overview for each $FRAMEWORK to be "learning the $FRAMEWORK".
Web components, OTOH, have less functionality than full-blow $FRAMEWORKS, but each can be individually learned over a lunch break, reused by non-JS-developers and is a knowledge base that is more or less future-proof.
What you definitely can not learn over lunch breakis whatever local developers created as a local ad-hoc framework. Those area always more time consuming to work with, because you have no internet, stack overflow or documentation to help you.
Yes. JSX is extremely complex to support and it is absolutely the worst part of react.
Really now. Most devs who think to themselves "man I wonder how useState works internally" will arrive at the logical and correct answer within seconds. It's not complicated or magic. There are bare-bones implementations of react hooks (-ish) with a couple hundred lines of code. Implementing a basic "useState" and "runFunctionComponent" is easier than most third-semester CS assignments.
If that's the bar for magic, I'm surrounded by 20th level Warlocks coming up with new Eldritch Invocations for fun over breakfast.
(Just for completeness sake, Haskell and alia sometimes might get away with less lines, because they tend to be “wider”. Word-count is a better metric here)
The answer is shockingly dumb. To be fair, I checked the code years ago, but basically almost every hook calls useReducer internally and useReducer uses an array stored in a global variable that is set before your component code gets called and the order of the hook calls determines which hook gets what useReducer state.
It's basically a giant kludge and the React developers and users pat themselves on the back how "functional" their components are, when in reality they are flinging global mutable state like crazy.
This is true for literally every useful computer program ever. At some level it operates exclusively on global mutable state. Doesn't mean your high level programmer-facing interface has to look like that though.
I'm not sure it is. There is a global dispatcher that finds the right component state to use and increments a counter to ensure that the correct state "slot" for that component is used. There are also a bunch of mechanisms to prevent slot mismatches. I guess you could make a loose analogy with writing custom allocators, except instead of the binding knowing the address, the order of calling useState determines the address :) Really not that simple.
Explicitly not part of that proposed task. We're talking about a basic hooks implementation with useState here, not how react deals with its tree of components.
For the sake of the task the signature of runFunctionComponent just needs to be
runFunctionComponent<P, R>(fc: (props: P) => R, props: P, hooks?: any[]): { hooks: any[], returnValue: R }
or even dumbed down to runWithHooks(runFC: () => void, hooks?: any[]): any[]
where runFC would wrap the call to the function component to pass the appropriate props and store the return value, freeing students from thinking about that. Also specify the function doesn't need to be re-entrant.In either case it won't need concern itself with what the component returns (children, whatever), matching children to tree nodes, or scheduling, at all. Would be nonsense to cram that logic in there anyhow.
What you were talking about would make a good later task: easy to get a basic implementation, but hard to get an implementation that isn't subtly wrong in some way. Plus you can slap on extra goals like supporting keys. Lots of room to score students beyond just pass/fail.
Warning: opinion incoming.
JSX is one of two reasons that I never liked React at all and wish it would go away.
I've been developing "web applications" since the latter half of the 90s. I've worked with everything from an in-house built dynamic template language (very similar to what PHP did in the early days) developed in C++ and served using CGI in Apache, to developing with PHP to Java Spring MVC to Django to modern frontend frameworks including both React and Angular.
JSX reminds me of what PHP was like in the 90s and early 00s. People adopted it widely because the barrier to entry was extremely low. We had all of these static HTML websites and "developers" could just go in and start adding control flow to those documents in order to add dynamic functionality. It was easy ... and it quickly turned into a complete mess so big that PHP ended up with a reputation as being a toy language that "serious programmers" wouldn't be caught dead touching. That reputation was a bit unfair but also kind of earned.
With the rise of PHP's popularity, the PHP "community" itself came to realize that they had a decent turing-complete programming language that they were working with, but that this idea of mixing business logic within HTML documents was a massive anti-pattern. It's ironic that what began its journey as a template language eventually realized that the best thing for the language was to stop using it as a template language, and to adopt different template languages such as Smarty in order to promote a separation of concerns between view / presentation concerns and business logic.
React was never created as a "framework." There are actual frameworks that are starting to come out that use React for its view layer, such as NextJS, but React itself is just a library that allows you to create custom components and then choose your adventure when it comes to what other libraries you want to mix and match. But because people reach for React to start throwing together dynamic frontends quickly, everything is compononent-first. So it actively encourages you to put business and integration concerns in what really ought to be your view / presentation "layer."
And JSX magnifies that problem by just being JavaScript. So, again, it's PHP all over again. You've got a very powerful turing-complete programming language that you intersperse with your markup and you get messes. Even in the most well-written, concern-separated React projects I still see a lot of "ugliness" that comes through with JSX.
I did mention that it was one of two reasons I'm not a fan of React, the other is that - again because it was only conceived of as a component / view library - there is no inbuilt view encapsulation / scoped styles. This problem is somewhat mitigated by CSS modules these days. But view encapsulation is something that I've always seen as Angular's killer feature - the ability to just style your tags directly and create component markup that is completely devoid of class="" attributes. Angular and VueJS actually trained me to hate 99% of class="" attributes and to think of them as a type of code smell... just because it is possible to create higher-level components that don't have any at all but still styling when needed.
Btw I'm sorry if I misunderstood what you're trying to say, because you're mostly dissing the class attribute which I very much depend on purely to keep my websites secure.
With frameworks like Angular, you can create a component and bind CSS styling to that component in a way that you can select just your HTML tags directly ... like style div {} instead of styling .my-card {} etc. And those styles are not "global", they are scoped to only those elements that are referenced in your component's markup.
There are two ways that this can be implemented and frameworks often let you choose which one you want to employ: Shadow DOM or emulation. With emulation, the classes and selectors get generated for you at compile time so if you inspect the DOM you'll see tons of class="" attributes that were auto-generated ... but the developer never had to write them or think about them.
The result is code that is so much nicer to read and work with.
If you have nice clean modular components, it doesn't matter too much where the code vs template boundaries are inside. They shouldn't be so complex that it matters. This is why I like JSX - for any given component, everything is in one place.
React won out because it has a low barrier to entry and is very unopinionated. That's not a bad thing. But don't pretend for a second that it won out as any kind of industry standard because it was technologically superior or was a better designed "framework" (again, it's not and never was a framework, it's a view library for creating components).
There are 3 broad concerns when it comes to programming for the virtual machine that we call the web browser:
- Semantic demarcation, which is what markup does: this is a paragraph, this is a header, this is card etc.
- Visual decoration and styling, what CSS does. This is how a paragraph looks, this is how a card looks etc.
- Interactivity and control flow: what JavaScript does. When the user clicks the button this is what happens.
Those are all distinct responsibilities which the browser handles separately natively.
We don't need to even start talking about MVC or OOP design patterns when talking about spearating your concerns. That's a basic engineering concept of isolating your moving parts to keep things simpler and allow you to change things in isolation without acccidentally breaking something else, which is the bane of software maintenance.
The notion that diluting separation of concerns is a design innovation that makes software development and maintenance somehow easier is truly baffling.
I worked through this whole era when the web platform was figuring this out. Or I should say re-figuring this out, since desktop GUI development had worked it out going back to the late 80s.
But I work in an academic lab. We don't have tons of money to maintain the apps that we've written. We need them to just keep working once funding has moved on to new projects.
We're just finishing up a rewrite of an app from Vue to Web Components. It had dependency rotted to the point where we couldn't update anything because of dependency hell. Rather than spend hours trying to fix it, which we've done before and would have to do again until the end of time, I decided to experiment with Web Components. The experience was immediately so nice that we went all in. No regrets. We went from ~15 dependencies to ~1 (d3js).
If you're curious to try the apps, old one[0] new one[1].
> DOM be a function of the state
I understand the benefits of the mental model, but the tooling that delivers that generally have a lot of complexity that isn't trivial to parse and understand. I think the benefit of being able to parse and understand your abstractions (ones you build yourselves or buy off the shelf like a framework) is there are always gaps in the constraints under which they had originally been designed; so you'll always need to understand how it works under the hood.
React's class-based components were simpler because the lifecycle methods were explicit, but the hook-based model is "easier"; however, the component lifecycle is still present. The demo code does look really clean, but in real life you quickly start to face the underlying complexity of the abstractions (e.g., useEffect, memoization, realizing how fine-grained your reactivity is is based on how you've drawn those component boundaries). You can write these optimizations yourself, or wait for the new auto-memoizing React compiler to land, but in all cases, the overall complexity remains the same (or is higher). And this isn't unique to React or other frameworks, and you'll always need to delve a little deeper.
I agree that the DOM APIs could be more declarative (e.g., declarative custom elements proposal [1], declarative shadow DOM, `@scope` CSS as another way to scope styles), and there is activity in that space, both in the W3 Web Components Community Group and the Open UI Community Group. I'm trying to get more involved in those discussions and I'd recommend everyone who cares about the web and how we build for it to participate.
I think the process of standardization for new features (through the proposals phases to finally landing in browsers) is the effort to raise the floor for all to build on top of. I remember when Promises were landing everywhere and feeling a little overwhelmed by what it meant to me because I was using Bluebird. Then the feeling of "maybe I can simplify what I'm doing and lean more on the platform" set in. Frameworks are a testbed for new ideas, and we can go further by trying to see what pieces we can pull out and standardize around (e.g., Signals proposal [2]), so we can go back to testing new ideas on top of that.
[1] https://github.com/WICG/webcomponents/blob/gh-pages/proposal... [2] https://github.com/tc39/proposal-signals
Thats not to say that maybe for your use case you could have not used it in the first place or that removing the dependency was a bad idea - just that if you do like Vue there doesn't seem to be anything forcing you to always be on the latest version.
Most cases where it becomes an issue is when people drop security best practices falsely believing their frontend/client validation is a security layer.
Anybody thinking updating your frontend solves your security issues has deeper problems.
Front end client frameworks now span the backend. Nextjs is the prime example. It has plenty of CVEs already.
I'm as annoyed by it as the next guy. The supposed vulnerabilities are almost never actually relevant for frontend code, and rather assume that you are running Node as a web server.
It's just one of those weird things that has resulted from how the JavaScript ecosystem has centralized around using node for everything (i.e. building frontend code as well as executing backend code - even though the two have kind of nothing to do with each other.)
You have $FRAMEWORK using $BUILD_SYSTEM and depending on $NODE_VERSION which is running on $OS_VERSION in $ARCHITECTURE.
Eventually, there will be a security flaw in $OS_VERSION which will force you do update the OS (which eventually will be a major OS update) and on which $NODE_VERSION might not run any more at which point you find out that $BUILD_SYSTEM doesn't run on newer node any more because it relied on a package that shipped some binary do do its thing and the source code of that binary doesn't compile on later GCC versions any more.
And now you're in deep-shit because of a very distant but yet super important security issue that brought your house of cards crumbling down.
This is not a theoretical issue either. I've seen this happen to applications with a JS frontend that was only as little as 5 years old. It was impossible to build any more and needed some serious changes because of underlying OS updates cascading up the stack.
The fewer dependencies you have and the smaller they are, the bigger is the chance that things keep running as you exchange parts of your stack when you're forced to and the bigger the chance that you will remember what you have to do to rebuild if disaster strikes.
I'm aware that for many people 5 years is a huge time span though and that you either start a new thing every year or two at which point none of this matters, or you're at the other extreme and run RHEL and are able to back-port security patches manually if the need arises at which point none of this matters either.
But if you're in the middle of the two extremes, then keeping up to date incrementally is super important and being able to do that quickly and easily is inversely proportional to the amount and size of dependencies.
An often used hack is containerizing the dev setup to decouple from OS versions, but that comes with its own set of downsides.
So I don't understand why VC ecosystem can't get by without frameworks.
Because browser APIs and elements are quite limited so you're likely to pull in external deps tht provide better interfaces etc.
Kudos on keeping external deps to an absolute minimum
All the examples I have seen use them to template some data into html. I can do that with handlebars already.
Am I missing someting?
1) You dont have to load an external library
2) Shadow DOM
3) Dynamic slots
That’s about it, honestly LOL.
I guess the main point of most browser APIs was to let apps use browser features.
This one actually tried to make a standard way for apps to use other apps. But they already had their own libraries so nyeh, thank you very much! LOL
There are a lot of candidates for “what’s wrong with modern web standards” but this fragmentation, which comes from a rather mathematical view of programming, is one of them. Thing is, a lot of web devs never studied computer science (even CS 101) and less than 5% live in San Francisco.
How will they ever understand web components.
Also they let you publish a UI component that works in every framework, without having to build 7 versions of it (or just exclude everyone who's not on React, or something like that)
When do you want part of your page to have different fonts, colors, everything from the rest of the page?
Like I get it if you’re sharing a component on different sites, like an embedded component or ad banner, etc. But it just gets in the way if you’re trying to do normal things that the majority of web apps need.
See https://developer.mozilla.org/en-US/docs/Web/API/Web_compone...
- Namespace isolation. Example: you have different components in the same codebase or otherwise meant to work together; you may want assurance that a locally defined style attached to class “foo” doesn’t have unexpected effects on other components which happen to use the same class a different way. This is commonly achieved with build tooling, eg by mangling class names.
- Cascade isolation. Example: you have an embeddable widget that you want to look consistent in any context, regardless of the styles of its parent DOM. This is achievable to some extent without custom elements, but they are a way to achieve it with confidence in a relatively straightforward way (at the expense of other limitations and complexity).
So the point isn't to have your web component be different from the rest of the page. The point is that you can pass in parameters to make an off-the-shelf component look how you want. However, exactly how much freedom you want to give users is up to the component author. It is possible for there to be too little freedom, true.
See here [1] for a concrete example of someone writing a reusable web component, and figuring out how to let users customize the styling.
[1]: https://nolanlawson.com/2021/01/03/options-for-styling-web-c...
Being able to make a new root for rems would be nice too.
https://open-wc.org/guides/knowledge/styling/styles-piercing...
If you want actual isolation of the whole web stack (HTML/CSS/JS) I don't think there are any alternatives to iframes.
I can't believe how extremely mad and frustraded I became when I found out - writing this out fills me with rage.
They are also good for style encapsulation, i.e. you could drop someone else’s component in your page and not worry about it affecting or being affected by your CSS. Anecdotally I feel like that is less of a common desire now than it was ~10 years ago, with the rise of both “headless” UI libraries (behaviour without dictating appearance) and the prevalence of scoped styles in front-end frameworks.
What does annoy me about the standard is that to use slots you must opt into the shadow DOM, which means that if you’re trying to create reusable components for your own stuff, you can’t style them just by dropping a stylesheet into the page. I’m sure there’s a technical reason why this is the case, but annoying nonetheless.
If you need several slots, there's an example duplicating that functionality with javascript in the second comment of this blog post: https://frontendmasters.com/blog/light-dom-only/
If you're using HTML as a document you can use web-components to include fancier interactive real-time feature
* A terminal emulator web component that attaches to a websocket * A date picker web component, add features like checking if a date is already taken * Custom form elements in general, a search-box that takes a URL for auto-completion suggestions * A map, but not a full mapping application * A data table, like the jquery plugin of old * Lightweight interactivity like tab widgets * Basically any of the custom components that jquery-ui provided
Yes you can do all of these without webcomponents, but the HTML is a lot cleaner and a lot more document like if it's a custom component. Mixing the model and scenegraph views of the web is not my favorite. It sure would be nice if there was a consistent library of web components available.
You can actually do pretty decent live-chat with something like HTMX and server-sent-events, I think. But it's sort of a progressive-enhancement view of HTML as a document model.
It's like jquery widgets but without dependencies.
And the browser vendors aren't in a hurry to remedy this situation.
For app dev (not library dev) web components are a super lightweight easy option when you stick with the light dom.
You can continue to use bootstrap or tailwind or whatever css thing you like, but get great functional encapsulation with near zero cost, especially if you use lit-html or something similar as a renderer.
My teams have generally found working with native web components refreshing. It takes a dev coming from the framework world about a week to adjust, and then they never want to go back.
Just using simple class properties and a manual call to a render function on set() gives you all the benefits of reactivity without all the hassle of frameworks.
The problem most people have with getting started with WCs is that there's not much out there showing how do to it in "easy mode".
Most of the getting started things throw you right into shadow dom, css parts, and all these really painful technologies that were primarily intended for use by library authors, not app devs.
I've been building apps with native WCs for a long time now, I should get off my keister and write a guide on how to make your life easier with WCs, something like "The Good Parts".
Alternative approaches that may work for your use case:
- "HTML web components" [1] - light DOM only, SSR-first, good as a replacement for "jQuery sprinkles"
- "Shadow gristle" [2] - use as little shadow DOM as possible. If you need styling or composition, put it in the light DOM!
The best part is not having a dependency hell situation every other week.
If you consider writing about this I'd like to read it. Or listen! Do you have a blog? In the process of gathering various comments about this technology as it appears there are blessings and curses, depending on how you use it.
This cost will compound over time where new frameworks emerge, and components get stuck on older versions of their frameworks.
I can't see this as anything but significant, and not to be minimized. Having multiple redundant libraries on a page is not the direction I would advise anyone to take, particularly not when baked into the accepted best practices. This bodes poorly in the long term.
I've listened to the arguments from web component advocates in blog posts, social media, and videos for years now, and I should be in the target market. But on top of the interop tax, they're full of negatives that aren't present in the mainstream frameworks.
Interop works great within each framework's ecosystem. The same dynamics that cause developers to seek interop cause them to huddle around a small number of mainstream frameworks. So we get a few vibrant ecosystems that push the state of the art together. Web components cannot keep up on the tech side of things, and introduce a ton of complexity to the web platform - ignorable to me as a dev, but not for browser implementers - in service of their early 2010s designs.
IMO you can because it’s the vast majority of webapp usage today. I’m also a heavy Svelte user and I love it but front end web dev is practically a React monoculture so it makes sense to think about it when evaluating options.
I’m not saying it isn’t a problem inherent in web components, it is. But using it as a reason to not adopt web components runs contrary to the logic the vast majority of the industry currently uses. Perfect as the enemy of good and all that.
You won't get a Svelte to look past the flaws of web components by saying "React is bad".
The article we’re discussing is titled “Web Components are okay”, not “Web Components are better than Svelte for webdevladder and their users”.
Like I already said, I use and like Svelte. But the vast majority of the web dev ecosystem uses React. Web components would be better than everyone using React. Arguably everyone using Svelte could be better still but that’s a separate debate.
> your response brings up React as if it somehow changes that.
It does. Because the industry clearly has no problem with a large upfront cost, given that it imposes one today. Web components would be better than what we have today even if it isn’t the ideal.
Svelte and Lit rendering code size just keeps going up, and up, and up....
You can argue about which is better, but this kind of naive size comparison is disingenuous.
Because they probably did the "several runtimes don't matter" thing, and every tiny component loads the full lit runtime
This is just not true.
Edit: when it was "unveiled" the sum total of JS was 178 requests totaling 1.37 MB: https://x.com/dmitriid/status/1777404560316707052
The tweet you quote is about whether web components are "useful primitives on which to build frameworks". I doubt many web component fans (who actually really used them) would say that they are. They're a distribution mechanism, and the only alternative I've seen from these framework authors is "just make the same library 7 times, once for React, once for Preact, once for Svelte, once for Solid, once for Vue, once for vanilla JS". This is awful.
I think people got trained by React into thinking that frameworks are big. SolidJS is 7kb, Lit is 5kb, Svelte is tiny and used to have no runtime at all, etc. Only React is big. And, well, if you're writing React components and publishing them as web components, it's usually quite feasible to build them with Preact instead, which is tiny as well.
So on a page with like some hodgepodge of 5 frameworks purring along inside various web components, there's still going to be only 20-30 kb of extra overhead. You can compress one image slightly better and save more than that.
On the other hand, if a component is small and focused in scope, it's likely either written in vanilla JS (like https://shoelace.style/), or made for a single framework (like the average React infinite scroll component).
In other words, I don't think you're wrong, but I do think you're prematurely optimizing a problem that doesn't really exist in reality. And the cost is big: if you get your way, every component author needs to either lock themselves into a single framework's users, or make 7-8 different versions of their component. I'd argue that that's much more wasteful than a few kb extra framework JS.
And then each component will have the entirety of that framework packaged with it when you distribute them. Unless you take special care
I did see one very badly configured app pull in six copies of Lit once because their bundler wasn't deduping, but: 1) that's still less than a React, and 2) an `npm dedupe` run fixed it.
- Not every web app is perf-sensitive to every extra kB (eCommerce is, productivity tools typically aren't)
- Plenty of frameworks have tiny runtimes, e.g. Svelte is 2.7kB [2]
- I wouldn't advocate for 100 different frameworks on the page, but let's say 5-6 would be fine IMO
No one is arguing that this is ideal, but sometimes this model can help, e.g. for gradual migrations or micro-frontends.
BTW React 17 actually introduced a feature where you could do exactly this: have multiple versions of React on the same page [3].
[1]: https://nolanlawson.com/2021/08/01/why-its-okay-for-web-comp...
[2]: https://bundlephobia.com/package/svelte@4.2.19
[3]: https://legacy.reactjs.org/blog/2020/10/20/react-v17.html
As far as performance we built out a data table for our DB GUI that can load in hundreds of thousands of rows and the scrolling through is still buttery smooth.
We actually are getting ready to release our web component library, it's a bit early and rough around the edges but would love to get some more eyes on it! www.astra-ui.com
No wonder the front end is such a disaster with those mindsets running the show.
And the wheel that they're at is the one in a clown car. Which is driving off a cliff.
Just having a custom element `<my-include-html remote-src='...'>`[1] is enough to forgo 50% of the reason for using a front-end framework with a build-step.
[1] One that executes scripts, allows `<style>` tags, etc.
Something like:
<div hx-trigger="load" hx-get="/header.html"></div>I don’t think a lot of people know about them, or if they do they have just heard about it in passing and have never actually used them.
Whatever the reason is they have a marketing problem that’s for sure.
There's a lot more options now, it would be good to have a way to have the best stand out.
Web Components are seriously cool and worth looking at.
Another confusion might be expecting an employer to educate you. That’s part of it but not the requirement.
Self-directed learning is critical to go with any formal learning.
First principles are needing to be taught where or before people are learning things like react. But they don’t, and get pulled into a world of complexity.
It was a joke tho, I use custom elements heavily but never with ShadowDOM. It is indeed a PITA.
for which problem? They don't replace the need for a framework nor do they make writing in one easier. They don't make dev ex better. What is the actual use case? If they were highly useful, they would be used.
Maybe they aren't useful because the modern trend of web development is to not care about speed or size as much.
Especially earlier on, web components were implemented without incorporating some of the major feedback from developers. What resulted was a low-level tool with some really awkward warts and APIs that was more-or-less unusable. (hence, one of the reasons why when web components discussions come up, lit is always mentioned since it needs that extra layer to paper over some of the warts)
TL;DR: it replaces some of the bad parts of Angular/React/etc. with its own set of bad parts that become more obvious only once you get deeper in.
It's not about better or worse for you, maybe for the average person.
This is a selling point only if your job is producing component libraries. Otherwise, if you're an application developer, you'll be using a framework anyway.
- renders very weird on my iPhone iPhone 15 Pro Max in Safari 18.0. Consider responsive design for smaller screen sizes to restyle the sidebar and set a body max-width instead of a width. You might not expect your users to develop on phones, but you might have a hard time with adoption if the docs are mobile hostile.
- the “explore components” button at the bottom of the home page seems to link to nowhere
- site claims “Learn from well-structured, accessible component implementations” as an advantage but I didn’t find any links to the implementation from the docs
- site claims “No dependencies to manage or update” but isn’t astra-ui a dependency? It has a changelog (https://www.astra-ui.com/changes/). Likewise “Full control over the code and styling” I don’t understand how I can both have full control but also be taking a dependency on implementations provided by a library.
I’m curious why you’ve decided to release this library? I’ve come to view open-sourcing internal software as useful to the company for a few specific reasons but to generally be a time-sink without much return unless it’s accomplishing a goal. Component libraries need to fight for general ecosystem adoption or there’s no audience and you might as well not publish at all.
Maybe rough around the edges is an understatement haha, we are actively working to make the docs here better.
So a few things, these are very primitive components that can easily be updated and restyled.
There’s a given that there will be a dependency when using a library, right? The thing is with this you don’t even need to npm install if you don’t want to. Just plug and play.
As far as why… there isn’t much out there in terms of a web component driven component library and I think we’ve done some great stuff with ours. That plus we have customers embedding our components into their platform and it’s always helpful to see the source code.
I hear you on the docs quality though we will work on that.
No frameworks, no reactivity. If you click a button, you had to update everything on the screen with event listeners manually.
Took the guy years to write it. It's like a monk got locked in a cell for years with a basic book of javascript.
I started by refactoring into web components, because I had to do it piecemeal. It's been a big help, and I've cut 50k lines of code so far. But the real point was to just learn everything the old code was doing before I start a rewrite.
Yes. The key to making an old and arcane code base understandable to yourself, is to refactor one small part of it at a time. That you're redoing with web components is just one way that would achieve this. I applaud you for not just throwing everything it away and starting anew.
On risk of this approach is that you get some way though and then move on to some other project, and someone else comes along and inherits the old project and starts refactoring in their own style, and so now you have an old arcane code base in three different styles... it still beats a rewrite of a 250.000 line code base in one go.
Broadly I agree with Nolan, though. Web components have enough rough edges that they're not going to take over the world in the current state. But, they are pretty nice for certain use cases.
I'm not sure what he means by not playing well with server side rendering though. I've been doing that without issues.
I wonder what makes you say that. All that I've seen seems to indicate[1] that Javascript is needed in order to register the template with a specific tag.
[1] https://developer.mozilla.org/en-US/docs/Web/API/Web_compone...
> This won't appear in your page until you grab a reference to it with JavaScript and then append it to the DOM
https://web.dev/articles/declarative-shadow-dom#how_to_build...
And the example below that one shows how to hydrate declarative shadow roots with JS custom elements, so the path to progressive enhancement should be clear.
I say alleged because I haven't had the chance to try it beyond a toy example.
This is a declarative shadow root [1]. The template goes next to its slot contents, so you can render both separately on the server and let the HTML parser attach the shadow root, without JS, then use JS custom elements to progressively hydrate it.
[1]: https://web.dev/articles/declarative-shadow-dom#how_to_build...
Now you are right about that you can use template without js. But I have to ask: what is the point of declarative shadow root?
The point of template is to be reused instead of being used once at the same place that it's declared. This seems like adding more markup and introducing shadom dom for absolutely no reason. You may as well just write the markup directly .
Different server-side instances of the same components won't necessarily render the same shadow tree, so client-side reuse was out of the question for declarative shadow DOM. But unlike client-side template, you get to paint components much sooner, during the parser, or even without JS enabled.
As for why a shadow root instead of the markup directly, well, that's kinda like asking why use web components at all, which I thought was part of the premise of this reply chain.
Even without JS, the shadow DOM gives you scoped CSS and isolates the shadow elements from outer DOM and CSS selectors. And custom elements, the other half of web components, can be used in HTML before defining them in JS, at which point you get to enhance the entire page at once, so now you can progressively enhance properly isolated components possibly written with different frameworks.
that's way off. Web component is about encapsulation/reuse of code & markup.
I am willing to entertain the idea that at some point, some people with authority did think the shadow dom was a key part of web components. However, as of now, if you just scan comments in this entire thread, everyone think it's an annoying abstraction that unfortunately people have to deal with in order to enjoy other benefits of web components.
You better do, literally every authoritative description of web components lists shadow DOM, custom elements, and template. And the last one is kind of a remnant of the push for HTML imports, which went nowhere, and is currently insufficient for anything you'd use React for, so its main value now is how it's being repurposed for declarative shadow DOM.
So, personally, I'd say they're about shadow DOMs and custom elements, whether that's useful or not; I'm not convinced either!
Most people who think turning off JavaScript is an important use case are only inflicting it upon themselves (and aggressively evangelizing that other people do it too) as performative luddites, to feed their martyr complex, so they have something to whine and complain about how the web is so unfair and out to get to them, and have exactly zero customers or products to support.
With reactJS I know I can server render a component or template and I’ll say JSX makes a pretty good tempting language.
With web components as far as I understand there is no good templating language built in anyways (so you have to bring your own) I’m not aware of a standard mechanism in which I can take some chunk of JS for a component associated with a tag and have it render out HTML that does not require JS to run.
> Maybe in the future, when you can render 3 different web component frameworks on the server, and they compose together and hydrate nicely, then I’ll consider this solved.
[1]: https://nolanlawson.com/2023/08/23/use-web-components-for-wh...
Guess I've been doing a bad job for a long time now.
“All [UK] government services must follow progressive enhancement, even if part of the service or a parent service needs JavaScript”
But more seriously, it's okay to do a bad job if you're being paid/forced to do it by a for-profit entity. That's what jobs are. Being paid to do things you wouldn't do otherwise (like making a webpage entirely inaccessible to people with screen readers because there's no text in the custom-elements pre-JS execution and not caring because the visually impaired don't contribute significantly to profit). Just don't chose to do a bad job for personal stuff.
Please, please just consider putting actual text in the HTML. It helps a lot.
PS. And not even mentioning Madnesscript which deserves another chapter in this story of horror.
But there seems to be also an interesting intrinsic reason: The versatility of the digital "Document" paradigm itself. Text in its digital incarnation (a sequence of human readable strings) is not just your usual text. While printed text on a page involves the encoding and annotation of human language sentences you can encode a lot more things in human readable HTML/SVG/XML formats (e.g. numerical data, visual geometries, declarative UI etc.).
The HTML family of documents is in this sense a sort of "super text". Its structure reflects already its huge dynamic (rich text) potential. But clearly you cannot solve everything in a declarative manner (though you should probably push it as far as possible). At some point the flexibility of code is important and the question then is what is the optimal way to do this (as in: easy, performant, versatile etc.). Alas at the moment we seem to be almost at the extreme opposite: everything as code.
Ideally one would refactor all these declarative formats that have proven their utility and resilience and rethink the role of code / frameworks, taking into account all the developments and learnings of the past decades (web assembly, mobile, pwa etc.).
That leaves minor feature additions as the only way forward.
For example, one of the deal breakers we faced was the inability to unload and reload a web component. Once you load a web component you’re stuck with it until you refresh the browser.
You cannot have an SPA with one page loading 1 version of the web component and another loading another version without some ugly namespace mangling.
I also like the Lit Framework (https://lit.dev/) from Google which is rarely mentioned but it is quite nice for some of the simplifications and extras you might need when building them but it doesn't get in the way or try to take over your entire domain with dev-lockin.
Whether going direct to WebComponents or a higher level simplification like Lit, they really are a freedom from dev lock-in that is nice to see.
I agree with Nolan here that the performance is fine. People keep comparing web components to React or Solid components, but the latter inherently have a tiny granularity whereas web components is primarily a way to distribute reuseable elements, not an application framework on its own. Don't make every tiny piece of your app its own little web component (or, at least, don't do it without a framework such as Lit to skip the pain). But web components are the way to build a component once and have it usable in all web frameworks (including none at all) out of the box. That's fantastic! And also unprecedented (on the web, that is).
It bothers me that so much of the discussion is still about whether web components are good primitives to build frameworks on top of. No, not really, they're pretty awful for that! But for distribution, nothing else comes close.
I'd love it for some alternative standard to emerge, without all the awful design choices of web components. And I agree with Rich (Svelte) and Ryan (Solid) that WCs being built into browsers are getting in the way of some other collective component interop design emerging. But until the framework authors stick their heads together and invent a fast, modular, non-shitty, property-only, functional-smelling standard for distributing components, I'm sticking with web components. For component authors, the only alternative is making a React version, a Preact version, a Lit version, a Svelte version, a Vue version, an Angular version, a Solid version and a vanilla JS version of the same UI component. That's awful! Web components are clunky but they're here, now!
There are plenty of programming languages where you can't do that. If you really need that, can't you just register it as "my-thing-1" and then register the other one as "my-thing-2"?
> If you really need that, can't you just register it as "my-thing-1" and then register the other one as "my-thing-2"?
Yes you can, but it means you gotta search-replace something, whereas in every modern programming language you just do an import or an alias or something like that.
And you still can't solve hot-reloading a web component that way.
> You can always add another layer of abstraction to solve a problem but removing one can be difficult.
The argument being that there's no need to add anything to the browser stack and make it even more complex when it doesn't add any essential capability. There's already JS making everything possible; and yet, they keep on piling stuff. When with custom elements specifically, you also require JS anyway (to declare them).
> Elements are not components.
Idk maybe for "web devs" the concept is difficult to grasp that HTML isn't for them. It's for text authors, and as such a markup vocabulary where low-level elements are placed next to complex custom controls nilly-willy isn't really a useful evolutionary direction.
(a) When you instantiate your View class (ie: 'web component') from JS, you probably want your Model and View to be 'owned' by properties of the Controller (eg: con.model and con.view). However...
(b) when an HTML tag instantiates your View, the View has to create its Model and Controller. And now there's no obvious place to store a reference to the Controller.
As a result, you have either to stick the Controller in a global variable somewhere, or - more likely - end up, not just with 'con.model' and 'con.view', but also with a new property: 'view.con'
So... two paths to create everything (Controller-based, or View-based), and this ugly '.con' property stuck in the View.
But, aside from this gripe, Web Components are okay.
So trying to do MVC with just web components feels a bit weird, at least to me. You'd need something extra I think
Perhaps I should try making my controllers the HTMLElement subclasses, instead of my views. My gripe remains: the best approach is not obvious.
So I have now explored using HTMLElement as the Controller, its shallow DOM as a data source for its Model class, and its ShadowRoot as a subclass.
It is clear to me now that that design would be the most natural fit for MVC.
However... it is also impossible. For whatever infuriating reason, they designed custom element's ShadowDOM class to not be subclass-able. You can neither give a ShadowDOM subclass a constructor, nor does HTMLElement reveal any mechanism to 'attach' a ShadowDOM subclass.
Fantastic /s
1. I wish you could reliably parse their attributes in the constructor instead of connectedCallback, so that they play better with typescript readonly properties
2. I wish I could declare properties in the class and have them automatically map to an attribute
3. I wish I could construct them using html instead of manipulating domnodes manylually
4. I wish I could construct child nodes first so that I can compose webcomponents with other webcomponents
As it stands webcomponents provide the ability to construct a single element at a somewhat predictable moment in time, automatically. That's cool, but their interface is lackluster to me
*Supports "Autonomous custom elements" but not "Customized built-in elements"
> I follow people on Twitter that I disagree with because I want to be exposed to different points of view.
Here is another good opportunity for it.
The religiousness which used to be restricted to programming languages has expanded to include frameworks, databases, test runners, infrastructure, CI tooling, libraries; literally everything is a religion nowadays.
It makes almost all technical arguments disingenuous and pointless. Like people arguing about whose religion is more correct. Observable facts don't matter so much.
People see arguments in favor of some alternative stacks as a personal attack against their career.
It's probably because companies themselves have become so focused on hiring based on these stacks. They have become a matter of livelihood.
In the old days, many companies saw software engineering ability as a separate skill; now they barely even use the word 'software engineering.'
lol
Geniuses build for The Scroll first. Retards build Web first.
(Don't misinterpret, I love The Web, but The Scroll is on track to be 100x better)
If you find React hooks hard to understand, then I really don't need to hear your opinion about anything, including about how web components and HTMX are so great. Just learn programming.
Because they are a decent fit to a specific set of problems (e.g. authoring components you can embed on different platforms, regardless of the technology they are built with).
I myself, as detailed in another comment, have concerns with web components, and having worked with them extensively, I feel they fall short in several aspects (especially regarding layout control, where they aren't isolated enough due to a series of design choices) but it's manageable.
A problem that I had in two different companies (embedding a set of elements that had to work in very different applications and required the host to know little to nothing) after much analysis was best solved with web components and they did well at that.
The possibility of doing `<script type module>import "yourcomponent.js"</script> <yourcomponent foo="bar"></yourcomponent>` without for us web component authors having to worry about the wide array of different technologies the hosts used (both at compile and runtime) has been in the end the best solution for both us and the consumers.
As Ryan Carniato points out, most of the criticism web components get is that their usage is misunderstood, some of their behavior falls short of their goals, and their name conflates then with framework's components creating further confusion, which are something very different.
The problem with comments like yours is that they add nothing of value to the discussion. We don't know if, when and where did you try to use web components, what hasn't worked for you and why, or if you've ever had the kind of problems where web components would've been a good fit or not.
You end up giving the hopefully wrong impression of an obtuse person lacking the engineering sensibility to understand the context of problems and of their possible solutions.
There are two different kinds of problems: those that nature imposes on us, and those that we create due to our own stupidity and ignorance.
Web components might help you to solve problems created by the latter (and in the process create more problems of the same sort). React, on the other hand, was a true step forwards for how to approach making user interfaces.
Web components are a packaging mechanism for custom HTML tags. If it were just that, fine, but the spec tries to make them into so much more, and this is where the problem and the complexity, and all the shit is. They are not units you should think in when designing your application, because they are not a component framework. They don't solve a single problem I would have when creating an app. If things like web components did not exist, your customers would not run a wide array of different technologies, all doing the same thing in slightly different but equally shitty ways.
There is no solution to embed additional common functionality that has to stay consistent besides iframes or web components.
The only alternative would be to implement the solution and all of its patches over and over.
I'm glad you don't have that problem, but if you have sensible alternatives I'm all ears.
Now that web components are part of the standard, they might be the more convenient solution for distributing components to heterogeneous environments, and allow for nicer packaging. But the price tag for that slightly nicer packaging is too high, and web components should not exist in the first place. Also, nobody in their right mind will make web components manually, you will need a higher-level framework from which you can generate web components. And I think one of the creators of such a framework gave you his opinion on this.
To summarise: web components are shit. The world would be a better place without them and the bloat of ill-thought out specs they bring.
React is a comedy of errors and imho should not be held up as aspirational https://medium.com/@fulalas/web-crap-has-taken-control-71c45...
Furthermore, there is really nothing to "learn" about markup. That is why it is markup.
I started to like React when we could write components with functions.
This make me into a bad programmer, I agree to disagree