Web components: from zero to hero
An introduction to writing raw web components
What are web components?
A Components lifecy...
For further actions, you may consider blocking this person and/or reporting abuse
@pascal Thanks for your detailed and informative article on Web Components.
You said: "It's perfectly fine to make web components using light DOM, but you miss out on the great features of shadow DOM."
I've just started playing with Web Components and am struggling with significant differences between using Light DOM and Shadow DOM. With Shadow DOM you need to use
<slot>
's to render markup from a parent web component in a child web component. In Light DOM<slot>
's don't exist and I've been unable to get parent markup to render in a child web component using Light DOM.Further they way you construct html in Shadow DOM seems to be different to Light DOM.
I'm using lit-element FWIW.
Others reading this note that Dev.to has a series of articles by Benny Powers which are definitely worth reading. Part 1 dev.to/bennypowers/lets-build-web-...
Hi Neville!
When writing raw web components, you can set the markup of the light DOM like so:
Notice how we omit attaching the shadowroot to our element, making it render to the light DOM instead.
In LitElement you can override the
createRenderRoot()
and returnthis
to make your element render to light dom instead. You're essentially changing the renderroot from the default shadowroot (LitElement defaults to using shadow DOM), to the light dom. You can see an example of that in action here:stackblitz.com/edit/create-lit-app...
Furthermore; yes, you are correct; when using shadow DOM, you can use slots to render markup from a parent into a child component. The slot API is part of shadow DOM, so you can't have one without the other. (eg: you can't use the slot API with light DOM).
Here's an example of using slots:
stackblitz.com/edit/create-lit-app...
And here's some recommended reading on using slots (the mozilla docs are an incredible reference for anything web components):
developer.mozilla.org/en-US/docs/W...
I hope that's helpful!
I can also vouch for benny's series, I've read them all, and they're all great and showcase really detailed and diverse examples of different web component technologies. :)
Edit: I'm currently working on part 2 of this series, which will talk more about lit-html/element.
Hi Pascal,
Thanks for your reply. I have been very successfully using Riot.js for quite a while in our Web App Clibu(clibu.com) and would like to move to using Web Components, hence my interest.
I starting playing with Lit-Element a few days ago and I am impressed so far, however as is often the case it is one step forward, two back. ;-)
I am aware of using Light DOM with lit-element which would be an easier initial transition, however I'm currently stuck on using nested components in Light DOM. I've put a sample at stackblitz.com/edit/create-lit-app... and you'll see that
<nested-light-dom>
doesn't display any content.Am I right that this isn't possible without using Slots and therefore Shadow DOM?
You are correct, you'd have to use slots and shadow DOM to achieve something like this. Do you mind me asking what your use case for this is though? Generally components that use light DOM are simpler leaf/UI components.
Pascal, thanks for the clarification.
What I find somewhat confusing with custom elements is they don't behave like normal elements. For example:
displays "Hello Neville" whereas:
Only displays 'Hello'.
I've put up a sample of what I'm trying to accomplish at: stackblitz.com/edit/create-lit-app...
What I want is to have a Web Component that expands & collapses the Web Components nested inside it.
For some reason I don't yet understand the demo app is not working correctly, ie. there is no animation for the transition specified in
card-element
and the height doesn't reduce to zero.The same sample without using
card-element
works perfectly. ie. Replace<card-element>
in<container-element>
with<div style="transition-...>
I assume that my current lack of knowledge with Shadow DOM will explain the underlying issue, or not. ;-)
Hi Neville, I quickly hacked together this example for you:
stackblitz.com/edit/create-lit-app...
I hope that helps you :-) Feel free to reach out if you have any more questions.
Hi Pascal, that's great and works perfectly and is simpler. Any idea why my code didn't work, just curious.
in
card-element.js
, change:to:
When querying for DOM in web components, you want to target the shadowRoot. Also, you were querying for and changing the height of the h1, while really, you wanted to change the height of the container div.
And you had a bunch of js you didnt really need 😛LitElement will take care of your properties for you. You can use your own getters and setters, but we didn't really need to here. I'll expand more on LitElement in part two of this blog series.
Thanks again, much to learn about shadow dom and LitElement. Happy enough though for my first week.
Can I suggest you include some downloadable examples in future articles. Stackblitz is awesome as well.
Often we just get pieces of Javascript which are great at explaining things, but when we try and run something like LitElement for the first time we get stuck with build tools and errors in the Browser until we get everything sorted out.
I hadn't found github.com/thepassle/create-lit-app when I started last Monday, which was a pity. I like Parcel.js which is what I'm using, but it took some time to get code that ran without errors.
I'll look forward to your future articles.
PS. My wife is Dutch. Amsterdam is great. ;-)
Yeah, takes some time to get used to web components, but when everything clicks they're well worth it (especially LitElement).
As for downloadable examples, you can find the source code of this blog over here: github.com/thepassle/webcomponents...
(You can run it locally with
python -m SimpleHTTPServer 8000
, or any other method of serving)Feel free to reach out if you have any more questions, you can find me on twitter, and usually on the polymer slack.
Hi @pascal ! First of all thank you for the tutorial, is by far the best web-components tutorial I've faced to!
I have some doubts that I would like to solve :)
In the section Events I don't understand why do you handle the index as attribute instead of property:
Also in this section, you refer to this.index instead of this._index:
Is it OK or is it a mistake?
And the last one. In the section Reflecting properties to attributes I have the same problem with setters that I had with index. Why do you handle the checked as attribute instead of property??
Many thanks in advance and congrats for your work!
Hi Rafa, this is mostly because attribution/property syncing. Arguably the index property doesn't need to be an attribute, but it's a good showcase on how to sync attributes with properties, and how to deal with different types than Strings.
If you'd set
set index(val) { this._index = val }
, the attribute won't be up to date with the property. When getting the index property, we can just read the attribute's value.Same goes for the checked property, you'll almost definitely want the attribute to stay in sync with property, and its a showcase on how to handle boolean attributes as well.
Hope that clarifies — feel free to let me know if you have any more questions 😊
Hi Pascal! Thanks for your quick answer!
I understand what you say, but I thought that it was necessary to maintain a "model" (based on properties) that is mapped to all the attributes. That's why I thought that all the getters and setter should point to properties, instead of attributes.
Would it be possible to do it that way? I mean, maintain a model (properties based) and at the same time maintain up to date the syncronization between attributes and properties?
In the other hand, I understand then that attribute-properties mapped is not always mandatory, depending or you component logic, right?
After a week with one dev pair trying to build a web component, this is the best post anyone on our team can find on the internet.
Every other web component tutorial seems like the author doesn't even have working code, and they miss important steps like how to include css.
Hi Pascal,
It seems the examples of the code running in stackblitz have no reference to index.js. It is never included in the page. How is the custom element code even running? The only script links are to node_modules.
Hey Lorless, stackblitz does some magic that adds the index.js. If you're trying it out locally you should add a
<script type="module" src="./index.js">
to yourindex.html
.Thanks! I wondered whether something like that was happening. The tutorial was good.
I'd additionally install es-dev-server as explained here. In case local development environment is not already set.
I've always wondered about web components and this well-written post seems like the perfect introduction to learning how to implement web components.
Hi Pascal, first of all it's a really nice article.
You said: We've currently set it only as an attribute, but we would like to have it available as a property as well. This is called reflecting properties to attributes.
Reflecting term is used vice versa or would be attribs to props ?
Hi Edson!
Good catch! That should be ‘reflecting properties to attributes’, we reflect a property to be available as an attribute on the node
tks man ... I found today another good explantion from aligator.io.
"alligator.io/web-components/attrib..."
Pascal, supposing you had a backend, how would you consume it? More prreciselly, supposing you need to persist the to-do list and validate each new to-do against some backend rule, how would do it? I guess you would need some third library, right? Which one would you suggest if you have to choose between ReactJs, Redux or litelement?
Hey — I somehow missed this post, sorry about that. In a more real life example, you could probably do a request to a backend using
fetch
oraxios
or whatever you prefer to use. You can use Lit-Element as your app/component model, and you can even add Redux to that if you need it.Hi Pascal, I have a few questions. In the github code example for this you have the following index.html:
github.com/thepassle/webcomponents...
I don't understand why you would need import('./to-do-app.js'). You have imported the component at the top and in-fact you have used it already in the html body.
I've stepped through with the debugger and it looks like the constructor of to-do-app.js is not even invoked until after the script is finished, this is despite the fact that the html is already used in the html body!
Can you explain what is happening here?
This is brilliant. Probably the only tutorial you'll need to start.
Good job.
Hi! Thanks for this extremely well organized and detailed walk thru of a basic web component. It has cemented a lot of disparate information for me. I get it now. Thanks!
Thanks, I'm glad to hear that!
What about observing a component size change? In theory
ResizeObserver
should work, but so far it fails.jsfiddle.net/h15e48fx/2/
Hi,
thanks for the great tutorial. I wonder how do you input font awesome symbols/characters to template directly, eg. the button:
?