Hacker News new | past | comments | ask | show | jobs | submit login
Ways to make a web component (webcomponents.dev)
350 points by kjhughes on July 17, 2020 | hide | past | favorite | 194 comments



I'm not into web development at all so this sounds very new to me, but aren't components in general meant to be self-contained so that they can be reused as "building blocks" for larger applications (or pages, i guess)?

Doesn't using a framework in this case mean that you also have to at least bundle that framework with the component? What happens if you want 10 components that each use a different framework? Or some of them use the same framework but different versions? What about the same version, does that get duplicated for each component or they get shared?

Also are these components meant to be "bundled" with your code or you are supposed to source them from their "home" server? In the latter case does that mean that a site using 10 components will open connections to 10 different servers (one for each component, assuming all components are from different developers)?

Or am i missing the point completely? TBH i think of "components" as in Lazarus, Delphi, .NET, etc (where you have reusable components like buttons, labels, input boxes, etc but also nonvisual stuff like timers, imagelists, etc).


Nope, your thoughts are all essentially correct. The answer is “yes” to all of them; even the ones where it seems that “yes” would be contradictory.

Bundle everything? Yep. Self contained? Yep. 10 components that open connections to 10 different severs? Yep.

The web is so massive that there is a way for pretty much everything (though for some reason webdevs try not to admit this).


> though for some reason webdevs try not to admit this

This is mostly because if you can keep development paradigms consistent within your job, that is one less thing to whack-a-mole in the fun game of "with permutation X of the endless combination of browser (and version), OS (and version), etc. why isn't the thing working?"


Except for the pure HTML5 solutions, everything else uses a JS framework.


I don't know why the sibling reply comment to this by golemiprague was killed.

My understanding is that he is correct - in terms of requiring a framework to be bundled with your code. Svelte is not a framework that needs to be bundled with your code, instead it injects helper code during the transpilation step and the result has no dependencies.

If I'm incorrect I'd appreciate the trigger happy person to explain why rather than killing this comment.


While they use a JS framework it doesn't mean the framework is used also in run time, in pre compiled frameworks like svelte the generated components are just vanilla JS and can be used later in any webpage.


Sums up "state of the art" web we're using right now = absolute mess, bail out if you can, save yourself, stay sane


The entire ecosystem got a bit too crazy for me as well, but there are a few sane places if you go looking. I settled on the Elm ecosystem after a long period of consideration. Probably the only good thing to come out of having too many options is the higher probability of finding one that suits your perspective.


Ignore what people say and just write everything yourself.

https://jsfiddle.net/4qkpLw3c/

ghee, that was hard????


i like the keyboard support :) +1


> Or am i missing the point completely

Sort of. These are ways you can built web components today with modern conveniences that some of these _libraries_ (not frameworks), might lend you.

The site isn't claiming these different approaches can all be used together on one page in a practical way.

Technically you could, but it would have the obvious downsides you mention. This is just the reality of the limitations in the standards today.


I've just heard about it, but Shoelace [1] is a component library designed to work with any and all frameworks. To quote from their website, web components address these problems:

  Unfortunately, framework-specific components fail us in a number of ways:
    - You can only use them in the framework they're designed for 
    - Their lifespan is limited to that of the framework's ⏳
    - New framework versions can lead to breaking changes, requiring substantial effort to update components 
There's another discussion on the frontpage [2] about Shoelace that addresses some of your questions.

[1] https://shoelace.style/

[2] https://news.ycombinator.com/item?id=23866894


So for the angular implementation, the compiler will only include the necessary parts of the framework with the web component. So you’re only including the bits you need for the component to be usable.

There are ways you can coordinate the components and their dependencies to rely on the same versions, but this will probably assume they are your components.

Svelte does something kind of similar where it doesn’t share the same bits between components, but according to the creator it the theoretical point at where it would be more beneficial to have share is so far off to be pointless. So presumably if you have something that efficient you won’t have to worry so much, I think we are a ways away


> Doesn't using a framework in this case mean that you also have to at least bundle that framework with the component?

yes something like react would be the main framework on the page. i guess this is showing how much overhead there is in each of the different of frontend frameworks and also what the code to do a relatively simple but not trivial example looks like.


Right on the money. In fact, unless this changed recently, you also can't use two components that both depend on different, incompatible versions of the same third component.


> Doesn't using a framework in this case mean that you also have to at least bundle that framework with the component?

You should depend on any dependencies (including frameworks), but you shouldn't bundle them, exactly so that when an app is bundled it can do the efficient thing and share dependencies.

> What happens if you want 10 components that each use a different framework?

One reason to now use frameworks, but use smaller modern rendering libraries, or none at all. In a large application it's probably not a big deal to use a couple of 3KB libraries - you'd still be far under a typical framework in weight.

> Or some of them use the same framework but different versions?

That should be fine and is a huge reason to use web components. When you have a stable component boundary you can upgrade libraries a component at a time, rather than needing to upgrade the whole application at once. Yes, you might have some overhead during the transition, but at least you aren't stuck. You might not even deploy during the transition, but at least you could. Framework transitions are super tough in monolithic frameworks and I've worked with teams that use web components specially to get around that.

> What about the same version, does that get duplicated for each component or they get shared?

Again, components should be distributed unbundled so that bundlers do the right thing. It's similar to not bundling React with every React component, or jQuery, etc.


I downvoted this because these are (in my opinion) entirely theoretical and not practical answers. This reminds me of the "React is better than Angular(js) because look how compact and clean the API is compared to Angular!" type arguments. This implies that "React apps" are going to be less complex than Angularjs apps but the reality is anything but: large React applications tend to be just as heavyweight and hard to understand as Angular ones once you need a router, state manager, and 20 or 30 other "compact" libraries to do what you're trying to do.

Likewise "When you have a stable component boundary you can upgrade libraries a component at a time, rather than needing to upgrade the whole application at once. Yes, you might have some overhead during the transition" I'll believe this when I see it, but I suspect you're making this sound way less painful than it actually would be. More likely each dev adds a new framework, they all move on to other teams/companies, no one is interested in painstakingly updating everything, and you have perpetual lock-in until you do the "JavaScript Shuffle" i.e. scrap it and rewrite in a new framework.

I'd love what you're saying to be true but my experiences tell me it probably is not.


> large React applications tend to be just as heavyweight and hard to understand as Angular ones once you need a router, state manager, and 20 or 30 other "compact" libraries to do what you're trying to do.

I largely disagree with the comment you're replying to in its oversimplification of things, however, this simply isn't true. 'Create React App' plus 'React-Router' is literally all you need for the vast majority of applications.

And you don't need "20 or 30 other compact libraries", unless what you're doing is specialised and requires them.

The idea you need "a state manager" is an unfortunate fallacy, due to React/Facebook's history by accidentally implying a Flux-like solution should be added to augment React, when in fact so much more could be achieved by simply following React's component model, and React's built-in mechanisms for managing state.


Aren’t form validation and http also separate libraries for React?


Only if you need form validation or http options the browser doesn't provide...


I've worked with a ton of apps migration frameworks in my career. From GWT, jQuery, Angular 1, and several non-OSS ones. The only successful incremental migrations I've ever seen have been via web components.

I'm working with one now that rather than convert from Angular 1 to Angular 9 in one shot is converting component-by-component to web components and working on features as they go. They'd have to stop features for at least 6 months otherwise.


I've been working on a webcomponent based reddit/patreon competitor for about a year now, and one thing I found was how incredibly easy it was to spin up my own micro framework.

I would highly encourage people to give it a shot. It's just going back to basics and working with class-based inheritance. It will take you a day to write out the basic functions you will need for 95% of your components, and you'll be left with a tiny library that not only solves your needs, but you know inherently.

If you're curious, lines 1-26 are all you need to easily bind an html block, a style block, and to do event/attribute binding via @/? statements: https://github.com/jjcm/soci-frontend/blob/master/components... . The rest of the file are just shared helper functions.


A friend of mine did the same thing, I did a small project in his framework and it was really fun.

https://github.com/ruphin/gluonjs

I'm not sure if he still maintains it or it's just done, it grew to about a hundred lines but it's quite comfortable. He used it himself to create a quite amazing portfolio page that unfortunately is down right now.


Don't be stupid, how do you know those 26 lines of code are free of bugs? They're not battle tested and you're just re-inventing the wheel instead of following best practices.


That's right, we should all follow best practices and use 26,000,000 lines of Angular code, which has been written and debugged by thousands of people, battle tested and wounded and scarred in wars we've never heard of and don't care about, and that we will never get around to reading all of.


Those 26 million lines of code have been engineered over the better part of a decade to perfectly cater to every possible use case. To not leverage those productivity gains would be insane. You wouldn't build your own car would you?

(/s, btw)


First contentful paint (FCP) is basically the same (+-10ms) from React down to a raw HTMLElement. I guess at some point appending elements to the DOM is the bottleneck, rather than the overhead of parsing JS and the efficiency of said JS.

To get a faster FCP, statically rendering the component to HTML and re-hydrating it in the client (using Gatsby, for example) seems like it will have a bigger impact than the WebComponent library used. Unless the client is bandwidth constrained of course.


Co-author of the blog post here.

My mistake is to call it FCP. This local benchmark only shows the performance of: - parsing the javascript code - creating the DOM node

That was the only goal.

We should have network simulation for the next update of the blog post to show some additional color. Good idea :thumbsup:

Static + re-hydrating is actually built in Web Components (technically). "Only" tooling like Gatsby or Next.js is missing to make it more developer friendly.


Thanks for clarifying! That's a fair point about web components having the capacity to be statically rendered and re-hydrated.


Would you consider comparing the results of a Lighthouse report?


Sounds good. Like have some sort of generic page with components and look for lighthouse score. I'm adding to the list!


Sweet, thanks!

Feel free to DM me if you need any help. twitter is in bio.


Web components to me represent the architecture I’d like to have. I think a good arch could be:

1. Build a rails/django type site, make everything work vanilla http+html etc.

2. Create standalone web components for the places you need some more interactivity, like type-ahead search box, “click to add” type thing, or a datatable

So most of your performance stuff could be handled/improved on the server, focus on aggressive caching etc. and most of the complexity would be on the server with small stand-alone parts on the client (ideally without a lot of interconnection, i.e. without a big client side state manager). This would not allow full-spa but I am not convinced SPA is the answer in many/most of the cases it’s used.


Have you heard about Phoenix LiveView? Sounds like what you want.

It's a Rails-like framework written in Elixir (piggyback-ing off 30 years of BEAM), and LiveView changes the game by allowing you to build rich front-end application from your backend.

The way it works is that it serves you static HTML on initial load (yay SEO), and then it establishes a websocket connection to the server (very efficient thanks for BEAM), and every subsequent user interaction sends a event_name which you handle on the backend, and the backend respond with only the changed data (Phoenix can intelligently track your changes so it only sends what has actually changed), and then morphdom on the browser side patches things onto front-end.

You get the full power of interactivity but you barely have to touch any front end at all (and if you really do, it plays super well with AlpineJS, so you still get that escape hatch).

All in all, LiveView is amazing (and the surrounding technology), I just wish more people know about it!


Agreed, Elixir LiveView is a lot like what @sequoia describes. I use LiveView to handle creating the more complex logic, data handling (and caching) on the server. For UI layout you can get surprisingly far with mostly CSS nowadays with small events back to the server just changing a CSS class here and there. Oddly despite the server round trip, the responsiveness is great.

Here’s a demo I built for a small wrapper around Bulgaria CSS. Running on a tiny VPS: http://lacritch.xyz/

Now Elixir/BEAM is an excellent platform for this as the average and 99.9th percentile response times are really consistent. So your UI interactions don’t usually take 70ms with every Xth one taking 400 ms, which would be really annoying.


Right? I use LiveView with Tailwind, and for those little extra interactions, Alpine works amazingly well. Such a dream stack! Alpine was created by Caleb for LiveWire (inspired by LiveView), which is why it works so well too :)



While not nearly as efficient, Turbolinks (and Stimulus for interactivity) is pretty popular.


Another one worth a mention is StimulusReflex https://docs.stimulusreflex.com/

Real-time stimulus + rails.


This is exactly how I work - albeit on my own projects - vanilla html/css Go and Postgres for all server side operations with a few modular web components here and there for the interactivity you mentioned. No frameworks, build tools or config files ...


It is very interesting that it includes the build size including compressed size, it makes me thing that one of the reasons that we don't have a popular repository of interchangable Webcomponents yet, is that if you commit to a framework to make things easier, then the size of the bundle is easier to justify if you keep all the components made within the same framework.

Importing components that have been made with different framework come with their extra bunde overhead, at least that what looks to me at first glance.


This is a fine state to be in now. The first priority of web components is to make components interoperable, so that at least you can use components built with different libraries together.

Meanwhile, the size of modern rendering libraries has dropped significantly, an order-of-magnitude less than React/Angular. This will continue as JS and web platform APIs advance. Proposals like Template Instantiation would finally give the DOM a good pattern for fast updates and reduce sizes even further.


unless you use HTMLElement


It infuriates me that the concept of composable web pages using small templates/components is not baked into the html spec and supported by browsers.

Every bit of code that we write in any programming language is made of composable bits which can be imported into other bits of code.

But we cannot do anything similar with html. every time any attempt at having composable component based html is made, it gets mired down in arguments, and we end up with Frankenstein monsters like Web Components, Shadow DOMS etc.

Web shouldn't be this hard. but the html governing bodies and the browsers have done a horrible of taking it forward.


Your frustration is understandable, but so too is why they never were -- HTML was never intended to be a programming language or component-based.

HTML was originally meant to be page-based. And pretty much everything added to it (CSS, JavaScript) is scoped to the page. (Some data storage like cookies is scoped to the domain.)

So shoehorning a "component" model to what is fundamentally page-based is essentially an impossible task to do elegantly. Either you wall components off in sandboxes and break the entire premise of the page having control ("why can't I modify the style of my component anymore with the page's CSS?!"), or your components are extremely leaky and accidentally stop working because of page-level CSS and JavaScript interfering.

So we have a ton of different component models that use different tradeoffs.

Obviously if HTML/CSS/JavaScript were being built from scratch today as a platform for apps, it would probably be designed extremely differently, very much around contained components. But that's not what it grew out of historically.


The sad thing about HTML is that its original markup was designed for academic texts (h1,h2, p), but now it is unused because every publisher uses PDF for that end


But now? Publishers, even independent self publishers, have overwhelmingly never left PDF as output of choice, as it is superior in terms of typography, consistency and layout. Take any (non-Print Replica; i.e., reflowable) Kindle book that has just a few logical symbols, and you’ll see they soon become sufficiently ugly to be distracting, and in some cases just unclear due to poor typography. I celebrate projects such as MathJax, but what is sad is that 30 years on MathJax should be necessary at all, and here the typography is better but still doesn’t feel completely natural as in PDF documents.

And from a practical, layman’s standpoint, now that we’re all becoming “mobile first”, while it’s natural to complain when a web link points to a PDF file, the resulting app makes the experience more immersive and self-contained, enabling any average user to save that document. Just saving an HTML document with all its linked material using your phone requieres a l337 browser, Evernote, Notion, or wget from a terminal emulator with the right flags... undoubtedly it’s a chore. I agree this is all sad, but by now I don’t see this changing anytime soon.


This is not true. It does not matter that HTML is page-based to be able to provide an abstraction for components of elements of the page.


The web components set of specs and proposals includes HTML Modules, Declarative Custom Elements, and Template Instantiation, the combination of which would give you exactly what you're asking for.


HTML Modules never shipped which is the biggest hole in the current Custom Elements ecosystem.


HTML, CSS, and JSON Modules are currently blocked by Import Conditions, which is going for stage 3 at the next TC39.



With type="module" and need to set ContentType they've invented new attribute and alias

    <script src="foo.wasm" type="module" iftype="webassembly">
but there is no need

    <script src="foo.wasm" type="application/wasm">
is not executed, so it can be used as is.

And "if" keyword means make a request with ContentType: "/", discard if result ContentType does not match. Which is ... strange?


TIL, that's awesome. I was under the impression they'd stalled out entirely.


I would 100% be in favour of an alternative to the DOM designed specifically for building rich application UIs. With upcoming features like portals it could even be embedded within existing HTML documents, or vice versa.

I’ve griped about this before, but I firmly believe that the trope of JavaScript developers needing a million dependencies and constantly reinventing the wheel stems from the fact that you have to do every damn thing yourself (else install a package or 50). The platform provides almost nothing (except, of course, the most remarkable distribution platform we’ve ever seen). The amount of hoops we all jump through to make building web app UIs tolerable is insane.

Want a filterable select that doesn’t make you type the first exact n characters really fast to find what you’re looking for? Gotta build it yourself. Hope you remembered to make it accessible and responsive!

There isn’t even an in-built option for pop-out menus, a feature of GUIs as old as GUIs themselves.

At the end of day the web is now as much an application platform as it is a hypermedia platform. We need better tools.


Not that it diminishes your general point, but are you aware of the datalist element? https://developer.mozilla.org/en-US/docs/Web/HTML/Element/da...


Damn, I actually didn’t know about that! Looks like it doesn’t enforce values like a select does, but I think that would be a lot less work to patch over with JS than implementing a whole custom component.


SVG has the concept of re-usable components.

Maybe in 10 years we can replace HTML with SVG.

hides

I'm sure that would make it much, much harder for people with accessible needs.


Accessibility is key IMO. And why it's sad the standard components don't get more love. Like why in 2020 can't select dropdowns do basic matching on any part of optiond instead of only leading characters? Or show filtered results as one types?


Reusing SVG elements isn't as easy as they claim either, and they seem to have taken some of the options away in SVG 2.0


Also SVG has also some very ugly things in the spec. And you would have to build up every UI-Element by yourself. It would have nothing to do with SVG, if it would be like HTML.


Years ago I heard someone saying that SVG should have replaced what CSS does nowdays, i.e use it for anything graphical. Not sure how well it would have worked though. But it is vector graphics so maybe very well?


I appreciate how Web Components are not opinionated meanwhile Component libraries are free to innovate and build on the Web Component foundation.


There are also simpler ways to write vanilla Web Components. Here's an example of the counter component written in what I find a simpler fashion. https://gist.github.com/rjsteinert/779d94dfc886723c2967a6f5f...


I like that this illustrates how easy it is to make a web component.

What I don't like is this also shows (via the render method) how you can foot gun yourself if you aren't careful. Just slabbing in everything via `this.innerHTML` will lead to gross performance problems, among other things. There's also no real diffing (only matters on more complex concerns, in a way) so you're replacing everything just to update the count.

I know we're all suppose to know better (I doubt you did this except for the express purpose of showing just how simple the Web Component API can be), but I've seen example code end up in production more times than I can count, from all over the web, so be forewarned stewards!


Wow, that's so much cleaner - why don't they just do it like this in the example? I'd be much happier learning standard web components if they were all a breath of fresh air like this.

Personally I'd miss the JSX syntax, but this more than makes up for it by removing the entire whacky state vs. props thing. Although- how do you represent custom props here - arguments to the constructor? That'd be really elegant.

Do those styles and query selectors leak out of the component? I'm assuming they don't since that's the entire point of the idea of a component, but just want to be sure.


This probably works fine for a simple leaf component like this, but this won’t compose well either, right? If every render completely overrides innerHTML then won’t all DOM state of child components get wiped? My understand is that this is a primary reason for e.g. React’s virtual DOM.


Exactly - the innerHTML approach doesn't work for component composition and it completely fails for things like forms, where you will blow away form input focus/cursor state etc.

This was a stumbling-block of the fairly simplistic predecessors to the React/Angular generation - like in Backbone.js if you chose to implement 'render()' in such a naive way.


That looks very clean - cool! I applied the redux pattern to WebComponents a while ago https://gitlab.com/f.parrillo/web-components/-/tree/master/s... .


Is gitlabs down?


No, broken link, the linked code isnt publicly accessible on Gitlab apparently.


CSS pollutes global without <style scoped> or shadow root. Even simpler variant:

    <form>
      <button onclick="form.counter.value -= 1;return false">-</button>
      <input name="counter" value="0">
      <button onclick="form.counter.value = 1 + +form.counter.value;return false">+</button>
    </form>
Custom elements is just a mutation observer with callbacks, can be used with DOM 0 as well:

    class MyCounter extends HTMLElement {
      connectedCallback() {
        this.attachShadow({mode: 'open'}).innerHTML = `<style>...</style><form>...</form>`
      }
    }
    customElements.define('my-counter', MyCounter)


Is there no Shadow DOM involved? love your way btw. Here is my no-class implementation from the vanillajs spa that I am working on

https://github.com/rishavs/Verdin/blob/master/src/views/comp...

I just wish that one of these days I will get off my ass and actually finish something that I start :D


Web Components are opinionated. You can't have an API that's not opinionated.

And the opinion has shifted from "this is the future" to "Web Component specs is aimed primarily at library authors, we don't expect people to author them by hand"


I also love this aspect. You can experiment new things on some components without affecting the entire application.


https://webcomponents.dev is actually partly build with Vanilla WCs, LitElement and StencilJS. No big deals ;)


I could have sworn several years ago, before React, Vue, Angular X started to pick up, that WebComponents were supposed to be that. That people were talking about how browsers would have built-in support for WebclComponents. I could be remembering wrong though.


Browsers do have built-in support for web components. You can extend HTMLElement in every current major browser.


This is in fact the first example in the article.


And such extension is a major PITA to do. The API is atrocious, like most of the DOM API is.


How is it atrocious exactly?

I mean it’s decidedly not React to be clear, but I’m not sure what makes it atrocious


It's overly verbose and error prone. Try adding an observable attribute. Then add another one. Then do something simple like change the name of the first one.

And that's just two attributes.


The Web Component APIs don't aim to be easy to use by application developers. That is simply not a goal of the spec. They're low level APIs which expose behavour and hooks on which libraries and frameworks can be built. As an application developer you are supposed to use those friendly libraries.


Aaand that's the problem with the API. The question was, how is API atrocious. Once an answer is received, it's immediately diverted to "the API is not for devs, you have to use libraries".

This doesn't make the API any less atrocious. And it would be nice if the platform didn't rely on libraries to make it usable.


Sure, from a dev perspective the API may be bad. But I'm saying that that isn't the right perspective to use when judging it.

"Nice" APIs or higher level APIs are desirable of course, but they tend to be much bigger in size and scope, much more opinionated and brittle than a smaller low level API. A portable API which browser makers can accurately implement and support for the long term, is a smarter goal even if it does expect devs to build a "nice" layer on top of it first.


In case of WebComponents it could have a small sane layer on top that wouldn't be too large. See WebComponents vs Polymer: [1]

As it stands now, it's nigh unusable by developers. Almost no frameworks even use it as their foundation. And those that do compile to them go out of their way to never ever use it anywhere except in base classes, maybe: [2]

As a result, today, 9 years (!!!) after they were first proposed, the very person who proposed them relegates them to "webcomponents will be used incidentally not totally" [3]

[1] https://twitter.com/dmitriid/status/865518972380237825?s=20

[2] Stencil compilation: https://twitter.com/dmitriid/status/1283445578853158917

[3] https://twitter.com/slightlylate/status/1283053719563563008


What's your suggestion for a better API?

You can't just say "React" though, you have to show how it actually integrates with the DOM that we have. "Throw away the DOM we have" isn't a realistic option either.


React integrates with the DOM we have. How dow you suppose it works?

How is React different from any of the libs and frameworks you are supposed to use for WebComponents? [1]

As for the API. How about a declarative API lime the one Polymer uses? [2]

[1] https://twitter.com/dmitriid/status/987771666846699520?s=20

[2] https://twitter.com/dmitriid/status/865518972380237825?s=20


Custom Elements was born in jQuery era, all it does is calls

    $('#myTable').DataTable()
automatically, there was tiny polyfill


In other words, HTML is unabstractable. I've been wishing to be able to type this since 1997.

<let mytag> <p>my tag!</p> </let>

<mytag/>

Same with css. There are workarounds we use because abstraction is a requirement to code. Somehow the web components spec even seems like a workaround, for which we have many workarounds... and around and around we go :)


The first step of that kinda exists, <template> is for HTML that shouldn't be rendered immediately. However, you need javascript to grab it and actually make it reusable.


It was different way of thinking though. It started with just a bunch of text lines. And then SGML is about to tag those text with .. tags. It's content-first layout.

Now we think about layout first, then putting text in it. Html is so old back then when browsers did not even have sense of having standard. Html tag is already good though, we now see it as sort of function, attributes are params. Turns out it started with really good design, but different way of thinking.


Doing this with Adobe's Flex, was very easy. And at least for some time, fully integrated in their Flex Builder.

But flash died, so did flex. (that means, the apache foundation still tries to reanimate flex with a js/html exporter, but I doubt it ever gets stable)


You're looking for XHTML


I don't recall components being in the XHTML spec.


XML:

    ...
    <counter xmlns="my" />
XSLT:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my">
      ...
      <xsl:template match="//my:counter">
        <form>
          <button onclick="form.counter.value -= 1;return false">-</button>
          <input name="counter" value="0">
          <button onclick="form.counter.value = 1 + +form.counter.value;return false">+</button>
        </form>
      </xsl:template>
    </xsl:stylesheet>
XSLT looks ugly but only because we should not edit plain text XML (same for markup). <template> is a limited version of XSLT.


Not in the 1.0 spec, but 1.1 brought modules. For example, MathML can be used as an XHTML module.

Mind you, there isn't exactly a 1:1 mapping between modules & web-components, with the former being 20 years old at this point.


is there any reason you can't write a web component with a <style> block, a <script> block, and a bunch of html?

the wedging everything into js is messy, to say the least. it seems the main benefit is being reusable and tightly scoped, but with randomized class names, you could tightly scope the aforementioned bundle as well. even better would be if html added 'for' attributes to <style> and <script> tags, like labels have, so you could target nodes in the dom by id.

perhaps being composable is a bigger challenge, but is it insurmountable with this approach?


I tried that using <template> <style> blocks, a few years ago.

my biggest problem was simply that I still had to add all my templates to a single long html file. And that really feels suboptimal. I had a scaffolded page structure at top. and then dozens of template sections following it. and then as some templates are supposed to call other templates (form inside page section) there was a clear hierarchy but my html file was flat and it was getting harder to reason about the structure of my app the more templates I added. I finally threw it all away and just did a vanillajs spa.

I really wanted to be able to breakup my html file into smaller composable ones without having to do that in JS. but there is no current solution for html importing other html files, and the spec discussion around html import are pretty much dead in the water (and have been since 2013)


> my biggest problem was simply that I still had to add all my templates to a single long html file.

The alternative (atleast when using frameworks like React and Vue with packagers like webpack is having it all in a single long html file. Which is just shifting markup from html to js. The other alternative is to dynamically load the html via ajax calls.

> I had a scaffolded page structure at top. and then dozens of template sections following it. and then as some templates are supposed to call other templates (form inside page section) there was a clear hierarchy but my html file was flat

Here you could make use of the document.ready event, and a js script that automatically parses and saves all <template> tag by id. That way, the templates calling other templates have a single source of truth for all dependent templates. You could also combine this will lazy loading via ajax calls.

> I really wanted to be able to breakup my html file into smaller composable ones without having to do that in JS. but there is no current solution for html importing other html files

There is no getting around JS if you want client side dynamic rendering - that's what its for. HTML is for static content, css for styling, JS for everything else.


thanks for sharing your experience, and agreed that being able to compartmentalize in separate, composable files is a desirable feature.


If you want single file components, you should look at the Svelte ( https://svelte.dev/ ). It also compiles the framework away, which is the reason it appears very high on that list when it comes to size.


cool, i peeked at svelte when it was posted about on hn before, and liked what i saw in the few minutes i spent on it. i should take a deeper look.


You can use @import to include shared CSS files. HTML import is still pending in the standard committee; should come soon. For now HTML template needs to go with the Javascript.

I usually structure a component like the following. The nice thing about WebComponent is everything is scoped to the component and you can have pretty generic names for things except the defined component name ("component1" in this case).

/components/component1.js

    import cu    from "/util/comp-util.js";
    import comp2 from "/components/component2.js";
    import comp3 from "/components/component3.js";

    (function() {
        let template = `
            <template>
              <style>
                @import "/styles/shared.css";
                @import "/components/component1.css";
              </style>
              <div id="the_component" class="component1">
                  <button />
                  <component2 attr1="green" attr2="10" ... />
                  <component3 ... />
                  ...
              </div>
            </template>`;

        class ComponentElement extends HTMLElement {
            constructor() {
                super();
                cu.attachTemplate(this, templete);  // the attachment boiler-plate code in util.
            }

            ...
        }

        window.customElements.define("component1", ComponentElement);
    }());


interesting, i look forward to when html imports come about. it's strange that this most fundamental component weren't among the first things you could import (i guess plain old links subsumed some of that need).


HTML Imports were actually among the first (shipped in Chrome), but then ES Modules/import/export took off and now spec authors want to reconcile the two before standarizing.


You can, using the template element: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/te...

I tried that for webcomponents, but I found more to my taste to actually have everything in my javascript file rather than to jump between files (it's not that messy if you keep your components smalls).


I experimented with this some years ago, building my own mini-framework for components. The main system provided a global hook that javascript within the template file could attach itself to, so everything for a single component did exist in a single file. So it is doable.

(I liked that part of my system, but I'd rewrite about half of it if I wanted to use it again)


neat, i remember running across that a while back, but promptly forgot about it. thanks for the reminder!


That's what Svelte does. I can't recommend this framework enough, it's easy to learn, simple, fast and small.


Two big points:

a. You can treat it as 'just Javascript' when dealing with bundlers (both complicated stuff like Webpack and simple hacky things like Gulp script concatenation).

b. For JSX and similar variants, because even the template part is 'just Javascript' with semantic sugar for tags, you can statically analyze it in a way that you can't with separate HTML and script content.


Problem here is CSS, JavaScript, and HTML. You might use variables, functions or attributes, styles, tags, that would interfere with other components.

That's why you want an additional layer like Angular2+ taking care of that.


yes, that's the scoping issue i mentioned, which can be solved other ways, like with admittedly-not-ideal randomized classes (or 'for' attributes, perhaps with css-style selectors rather than just for id's).


This is exactly how vue.js "single file components" work - a <template> with the "bunch of html", <script> and optional <style> blocks.


that's pretty close to what i'm thinking of, but still has some limitations (from https://vuejs.org/v2/guide/single-file-components.html):

• Global definitions force unique names for every component

• String templates lack syntax highlighting and require ugly slashes for multiline HTML

• No CSS support means that while HTML and JavaScript are modularized into components, CSS is conspicuously left out

• No build step restricts us to HTML and ES5 JavaScript, rather than preprocessors like Pug (formerly Jade) and Babel


You may be misunderstanding (or I’m not understanding your intent). Single-file components were made specifically to overcome those issues, as mentioned on the page you linked.


apologies, i did misread the page i quoted from. it does seem like i need to explore these single-file vue components more.


See the svelte and vuejs examples for what you describe.


Saw this idea on a previous HN comment thread and was intrigued by the idea.


RiotJS does this too


GPT-3 has an interesting contribution to this: https://twitter.com/sharifshameem/status/1284095222939451393...

Tell it what you want in plain English, then it builds you a web component in a random language that happens to be React.

I wonder if in the future we’ll be comparing the size of components generated by an ML model...


It's not random, it would have been part of the prompt, like his earlier prototype

https://twitter.com/sharifshameem/status/1282692481608331265...


co-author of the blog post here. Thank you @kjhughes for sharing our work here :pray: I don't know you but I love you already :heart: :)

I'm trying to comment where I have something to bring. If you have questions or maybe a list of things that you would like to see in the next update, please shoot at me!

Many thanks


I wanted to suggest that you could consider adding mithril.js to the list of libraries perhaps. It's a nice alternative to preact/svelte, and it's been around awhile.

-

BTW, When I go to https://webcomponents.dev/edit/uLX8C4bJf4Kmg2jWu1dL?pm=1 it doesn't load the component in the "Stories" tab for latest Safari or an old Chromium v68. Works fine in latest Firefox.

I see error message "Unhandled Promise Rejection: SyntaxError: Invalid character '\ud835'" on the worker.helper file in Safari. Chromium 68 ends up with "Unknown worker: undefined"


Adding Mithril.js to the list :thumbsup:

Thanks for reporting the issues we will look into it. How to you install a Chrome v68?


Many thanks for this, it's an excellent resource.

A suggestion for a next iteration: it would be nice to see which approaches work in IE11 / legacy edge. From looking into web components it seems those browsers are the major stumbling blocks, and I know stencil has an ability to polyfill things on those browsers, but I don't know about the other solutions.


Thanks for the nice words :thumbsup: I'm adding polyfills/IE11 analysis in the TODO list! Great idea.


Alternate title: The fractured state of web UI design explained using a simple counter.

I say that only slightly in jest, as it is a neat way to show all (or most) of your options.

Although it doesn't have my at-the-moment favorite way to make web components, blazor/web assembly. For me, it's made front-end development fun again, which I haven't enjoyed in a long time.


This isn't a collection of ways to "build components for the web" though, it's a set of ways to build "web components" the W3C component standard that ships natively in all modern browsers.

Even those these components are built with different libraries, they are all interoperable and require no framework to use them, so I'd say this points to the defragmented future of web development.


Edit: D'oh, I misread it, sorry.

You misread this post. The left panel has all the different popular libraries and frameworks that can be used to build a web component, including things like React, Svelte, Vue, etc.


I actually helped contribute to this list. If you look at the React, Svelte, and Vue, examples, they are specifically being wrapped as standard web components.


I've been working on project with Blazor for a few months. I still find myself feeling a little dread when I need to start work on a new big component and then I remember, not only will it not be painful, it will be pleasantly supersizing how easy it was compared to the 5 other time I built something similar in various JS frameworks over the years.

Only Clojure(Script) with Reagent gives me a similar good feeling, better actually, but I've never had a chance to use it at work. If Razor markup could somehow also be C# instead of HTML, like how Reagent markup is just Clojure, that would be neat, but I'm not sure that could be done in a pleasant way.


Blazor is fantastic. We've been using Blazor server-side for about 8 months now. The part that still hurts my brain is when I realize I am basically done with a new feature after I finish writing my business service. I can just inject & call it from the blazor component and directly draw whatever data without any mapping layer required.

I may have forgotten how to write controllers and juggle headers/query strings... It's really nice. Remember, every hour you spend writing some controllers or debugging query strings/serialization/status codes/et.al. is an hour not spent on your core business features.


Hi nend, co-author of the blog post here. I had blazor/webassembly in mind for quite some time, we will try to have it in on the next update :thumbsup:

IMHO Web Component is the opposite of fractured because whatever "helper" you use to make a Web Components, they sit on the same Custom Element standard. Somehow it's more convergence then having components in Vue and components in React that can't (without massive cost) be used together.


> fractured

Compared to the myriad ways for different cultures to make chicken with rice, this is nothing.


I love the simplicity of StencilJS. Not sure why it's adoption is so low but those guys have put a lot of work. Much better than what Google is doing with Lit Element.


Thanks! I wouldn't say Stencil's option is low. It's being used in some pretty major apps today, like Apple Music, Amazon music, and powering the next version of AWS Amplify components. Not to mention powering every Ionic Framework component which is upwards of 15% of the app stores (not all use latest w/ WCs however). One downside to that is npm stats won't show dependency network effects with stencil because it is compiled out for component collections like framework.


Whoops, meant popularity, not option


There are basic things StencilJS are missing, like source maps. Development has seemed to slow down on it as well.

There's also no types for web components. So, you can't statically analyze your code and know you are indeed passing in the correct property types.

There are wrappers for Stencil components for React/Angular/Vue, so you can ensure typings, but it goes to show the developer experience is far superior at the framework level.


Curious: how does Stencil compare to Lit Element?


The link should give you some answers on code style and performance. What else are you looking for?


I wouldn't consider Stencil simple. lit-html and lit element are simple, stencil is not.


'cuz Facebook spent a bazillion dollars to push the developer community towards React? They are really good at––how should I say this––controlling the 'community direction and sentiments'. Even countries for that matter.


Yup. Unlike the poor desolate folks at Google who ... checks note ... dominate the narrative on Web Components. And are, for example, consistently adopting a general aggressive anti-framework, and a specific extremely aggressive anti-React stance.


I have a server-side rendering approach that can get instantaneous paint of arbitrarily-complex views after pulling down index.html and exchanging a single pair of websocket messages. Once the initial page is loaded, redraws are as fast as you can get packets between the machines plus 2-3ms for decoding. It involves some very unorthodox methodologies and has latency constraints (similar to Google Stadia hosting model), but there are serious upsides. Including completely untouchable load & response times.

I strongly believe we are stuck in a Malthusian Trap regarding the use of HTML/iOS/UWP/Android for clients and then an entirely separate stack of things for the server - Go/.NET/Rust/Python/C++/etc. This separation adds such a huge burden to the development and maintenance of any application. I argue that if we must have some division between client & server, lets move as much of the complexity to the server as we possibly can. It's so much more manageable when you have all of your state in 1 domain like that. If the client doesn't have to track state or reason about its views, the application implementation for each device can be trivial.

I don't think that HTML/JS/CSS/JSON APIs represent a good boundary layer. I think final rasterized views, viewport dimensions and client events do. JPEG/MPEG can be decoded incredibly quickly on most mobile client devices due to availability of hardware acceleration. You will never be able to write a website in the traditional sense that can outperform the speed at which you could decode a full-screen JPEG image and display it. Server hardware is also getting insanely powerful with the new generations of AMD CPUs rolling out. Encoding JPEGs using something like LibJpegTurbo on TR/Epyc is ridiculously fast. This is also a type of application that does trivially parallelize across many cores. As bandwidth becomes ever more ubiquitous, and services more geographically-distributed, there is a good chance something like this could become practical in the general case.

For now, this is all just a crazy experimental thing that only works for a narrow range of applications. But, I would strongly encourage other developers to start thinking outside of the box. As networks get faster and latency gets lower, we will begin to encounter new "step function" opportunities that fundamentally shift how we do business.


Granted you don’t go into much detail, what I can gather you’re describing sounds incredibly unusually by vast amounts of people.

Shipping web pages as rendered jpeg images is a terrible idea for many many reasons.


I assume that "narrow range of applications" doesn't include applications that use text, images, or selectable/interactive elements.


> as fast as you can get packets between the machines

This seems like a really big limitation


That sounds a bit like Vaadin: https://vaadin.com/


I'd be very interested in hearing more about your work on this technology.


When I was a young web-developer, I joined the WHATWG group. And one of my few contributions was asking for custom HTML elements.

I actually developed a framework myself in LotusScript ;)


Our company had been looking to move our project away from Knockout.JS and on to Vue/React. I started playing around with native Web Components just because I like the idea of not being locked into a single framework.

However, I also wanted to keep using observables/computeds so I wrote a quick library that ties MobX with Native Web components, but has a Vue-like binding syntax.

Yes I know it’s yet another library, but it mainly just provides for a simple way to use MobX (an established library) with Web Components (a browser standard), and nothing else. It’s still very new but it works and I’d love feedback.

Example fiddle: https://jsfiddle.net/agquick/d0tsqar6/ Github: https://github.com/agquick/elemx.js


You might not have to replace knockout.js the solid framework author actually has implemented a html rendering lib that has bindings for both knockout.js and mobx. You might find it useful https://github.com/ryansolid/dom-expressions.


I love this creativity :) We should have you in the next update of the blog post!


Thanks, that would be awesome! It really makes state management within custom elements a breeze. I'm hoping others who appreciate observables/mobx will take interest and help me grow/maintain it :)


Been working on this off and on for the last 15 years. Pretty much ended up where I started. Except the newer JS syntax is nicer than in 2005.

Control:

  controls.hello = e =>
    e.innerHTML = `Hello ${e.getAtttribute('x-who')}`

  <hello x-who='world' />
"Framework" just cycles thru the dom and calls control def with the element as the param.

To handle data, there's a window.data object, which is global data for the app. Its a proxy object. So when window.data is changed, for instance from the results of a fetch, any controls that specify that key - their set() function is called.

  <button onclick='data.count++'>Click me</button>
  <counter x-datafield='count'></counter>

  data.count = 0
  controls.counter = e =>
    e.set = () => e.innerHTML = data.count


Note: if you're building your whole page on top of a library, a larger library isn't necessarily bad.

If a library is larger because it does more, useful things, and this means you have to write less and/or smaller components, in a complex app the tradeoff is probably worth it.


Webpack tree-shaking and automatically chunking into multiple tiny js files, which can be selectively loaded only when needed, is also super helpful here.


I've rewrote the frontend of one of my personal apps from react to webcomponents lately. For me, the most interesting aspect was that it's part of standards, requiring no dependency, so it means that I can use this app for decades without having to maintain it.

For the same reason, only the first example in this page would do for me (the one based on standard without using any library). But I suppose that for actually publishing something, such library would be needed, if anything for polyfills for older browsers (but then again, we don't expect published work to stand for decades without changes, so maybe the call for standardized technology is not as strong there).


> For me, the most interesting aspect was that it's part of standards, requiring no dependency, so it means that I can use this app for decades without having to maintain it.

Doesn't really make sense because for JS frameworks and for the "pure" component the dependency is the same - browser. If i don't have to maintain a project i can just pick whatever is the current version of React/Vue/Other and have the project running as long as the browser doesn't break/change something.


Isn't React itself still basically written using standard JavaScript? Why won't it run in the future?


> For me, the most interesting aspect was that it's part of standards, requiring no dependency, so it means that I can use this app for decades without having to maintain it.

I mean, you can also do that with React. Just don't update the library. The reason a web-component for decades without maintenance is because you won't be using all the new features— same thing.


I think the difference is that when you design a full React app, everything sit on the same "React base". If you want to move to upgrade to a new React version everything needs to upgrade. And if you need to migrate to Vue or Ember or Svelte... everything needs to migrate (or move to some sort of microfrontend strategy. But given the javascript cost of some of these frameworks, it's not cheap to go this route).

Web Components have a longer life span as they can be used in another app or another version with another technology without been migrated. Makes more sense for expensive component that have been battle tested. You don't want to rewrite them.


React is built to interop with "general" JavaScript, so in theory you can use it with other rendering frameworks. I used React and D3 all the time.

Performance-wise, it IS better to stick to a single framework, so I see what you mean. Web components being implemented in the browser do mean the user doesn't have to download any more JS.


> so it means that I can use this app for decades without having to maintain it.

That seems highly optimistic to me.


Considering all mothballed APIs still supported in modern browsers I'd say it's accurate.


Did they improve the SSR story lately?


Why would you ever have the logic in methods in Vue? Just keep it simple

<button @click="count--"> <button @click="count++">


Because templates should be declarative.

Error checking code becomes much more difficult because of the ambiguity and special parsing that has to be done. Its not just the loader, but things like the ide extensions used.

Allowing small bits of logic like this makes for very nice one-liners and demos, but in the long term its well worth it to make the template as declarative as possible - ideally just a method name.


Super cool, thanks for this!

Now regarding the results: Are there any actual usecases where one would choose Angular over some other library?

From what i’ve seen from before and now this, i am not aware of a single reason why would anyone choose to use it, if they are not constrained by legacy codebase or if there are no stubborn developers who swear by it.


1) These are benchmarks just using a counter - a lot of libraries perform really well using these minimal POCs but fail complete in real world applications (whether due to library performance, missing features, etc.)

2) This is sticking Angular inside a WebComponent with the minimal amount to make a counter - but you wouldn't take a heavy library to make a counter. Imagine React + Redux + Redux-Observables + StyledComponents just to make a counter. It's like hitting a nail with a ICBM.


Vue is great to make small webapp. Also wikipedia chose Vue to remake their UI IIRC. React is even simpler, but I really hate it for technical reasons.


We are working to document Q.js out of our entire platform becauss it can be used to make much simpler to understand web components, which work seamlessly with SPA pages, events and more:

https://qbix.com/platform/guide/tools

https://github.com/Qbix/Platform


I saw at least one that uses "style scoped", which I still can't believe is deprecated.


It's too complicated.

Just look at how Svelte is doing components. It's way better and easier.


This is neat, but I wish there were more examples than just a counter. I would never use something like Angular, React, or Vue to implement something so trivial.


This distills the differences into the simplest possible example, which given the number of examples is incredibly useful.

Also if you're in a React project, yes, you would use it for a counter. Obviously you wouldn't use React for just a counter, but it's a great way to show the basics of working with each option.


>Obviously you wouldn't use React for just a counter

You probably shouldn't, but there are a lot of people who reach for a React component to do anything on the web.


Yes, the counter is simple.

One reason is obviously that writing something in 30+ different framework that you need to "learn" is quite some work :) (10x more that I initially thought :D)

The other reason is that a simple component actually amplify the cost of the framework. And if you think about it, with a more complex component, what you have is more HTML, more CSS and more JS but they are essentially always the same. Only the binding are different from one framework to another. So in the end, the counter is actually a good representation of the cost related to the technology.

This been said, to be correct, the benchmark component should use more capabilities like slots and events to really cover the wide range of capabilities.

This is something we have in mind for a future update of the blog post...


"One reason is obviously that writing something in 30+ different framework that you need to "learn" is quite some work :)"

Ah, this is where you can use the Internet to your advantage. If you ask for help in advance, you generally won't get it, but if you post something broken or dodgy people will come out of the woodwork to correct you. So you don't need to work so hard to be correct. Just post something. Anything.

If that still isn't enough for some particular framework, just post something less than entirely complimentary about it ("I'm not entirely sure this framework is the best thing since sliced bread") and you'll be sure to get support, with a side helping of flamewar impugnment of your motives, intelligence, and ancestry, along with pithy observations about your obvious inability to recognize sliced bread if it was placed directly on your face. If you can ignore the latter you can get a lot of cross-framework comparisons done with surprisingly little effort on your part.


It's amazing but you are probably right :D


Yeah, the example is far too simple to exercise the libraries and would be much better implemented without a library at all.

But then, this is a great part about web components: you can implement a single component however you want, including with no dependencies, and still use it like you would have if you built it with a full-featured library. You can use it in Angular or React even.


Can't wait for 2–5 years from now when most of these have failed, and the One True Way emerges.


Don't bother waiting. By that time there will be another set of frameworks.


I kinda disagree, I think people are more pessimistic about fracturing than is warranted. For greenfield work, it seems like people have mostly converged on React and Vue, which are conceptually fairly similar. State libs have changed, but again the concepts translate pretty well. Similarly, webpack is the lingua franca. Years ago, we had a new build tool every week, and multiple competing frameworks (backbone, angular, and ember) with very different approaches.


I don't think any One True Way will emerge. Svelte is coming out to challenge React and Vue, Alpine+Laravel LiveWire/Phoenix LiveView challenges the concept of needing a front end framework, WASM is challenging needing Javascript at all, and Snowpack/Parcel/Rollup are coming for Webpack's crown.


It would be great to include metrics on:

1. size of framework javascript required to load first

2. tooling to compile. For example, the Vue.js works without tooling, but the React w/Class requires command line tools to build before the site is ready. That's a HUGE difference, IMHO.


co-author here.

1. The bundle size barcharts shows the component size (in light blue) and the framwwork size (darrk blue)

2. The Bundle Analysis chapter contains a description of the build tooling use for each component.

Vue can be used without build tooling if you choose to use the Vue lib with the compiler included but we use the pre-compiled way for max performance and smaller Vue runtime (without compiler).


> Vue can be used without build tooling if you choose to use the Vue lib with the compiler included but we use the pre-compiled way for max performance and smaller Vue runtime (without compiler).

+1. And likewise, you can use React without a build step too, though of course it is slower. [1]

[1] https://shinglyu.com/web/2018/02/08/minimal-react-js-without...


Thank you. I failed reading 101: My fingers ran ahead of my brain. Very good analysis. I can see it took a lot of work and precise attention.


Can you go into any detail why vue components are so large in comparison to every other example in the `Estimated Bundle size` chart?


"Compare coding style, bundle size and performance of 33 different ways to make a Web Component."

I thought this was a joke at first.

But it suddenly made sense in the context of modern web frameworks and micropackages and all these different ways of doing the same thing.

But wait, I thought. Surely it was a typo and they meant to type "3" and fat-fingered it instead?

But no. It really is 33.

Which one of those 33 would be the best way of doing things for me? How much time would I have to spend evaluating enough of them?

I'm going back to HTML 4.


I was surprised by the low number too. I'm much happier being a backend developer where I have a choice of over 400 frameworks in over 30 programming languages:

https://www.techempower.com/benchmarks


Use Svelte and be happy.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: