Hi! Not really a question, but just an appreciation message. I haven't used the full "Lit" package a lot, but "lit-html" is incredibly useful.
I use it in almost all my personal websites. And when I don't use it, I end up reinventing half of it and realize I should have used it from the start. This command is in most of my projects:
I've never felt I'm using a framework or anything that deviates from Vanilla JS and valid HTML, which is why using it hardly causes any more cognitive load than using regular string templates and JavaScript functions. Which is something that I can't say about other frontend tools.
Another thing I like from Lit is that with the CDN bundle, it's straightforward to experiment and use all the features without needing a build step.
Lots of comments in here are about shadow DOM, so let me give my take in one place:
Yes, Lit uses shadow DOM by default (for good reasons, I think!) and yes you can turn it off component-by-component, but that does bring some challenges.
Shadow DOM is most fundamentally just a private branch of the DOM tree for a component's internal DOM details.
Frameworks have this implicitly with DOM defined in a component's template vs passed as children. But that distinction isn't visible to other code, the browser, and CSS. This is both good and bad.
The biggest thing this separate tree gives us is the ability to tell what nodes are "children" - those are the light DOM children. Once you can separate children from internals, you can build slots. Slots are holes in the internals that children get rendered into.
Without something like shadow DOM you can't have slots. And without slots you don't have interoperable composition and you don't have viable container elements. You need some way to place children in an element that isn't confused with the element's own DOM.
So to me, before encapsulation and style scoping, interoperable composition is the most important feature, and that's really why Lit defaults to shadow DOM on. Without it we'd need some special `.children` property and Lit's component composition suddenly wouldn't be compatible with everyone else.
But the style encapsulation is often a major pain for developers, especially if they're trying to integrate web components into an existing system with whole-page stylesheets. It's a big blocker to a lot of design systems porting to web components.
That's one reason I proposed something called "Open Styleable Shadow Roots"[1] which would let styles from outer scopes cascade into a shadow root - a way to break open style encapsulation but keep slots. It's been hard convincing browser vendors that this is needed, but I'm holding out hope that it makes progress soon.
Nice to see you here and THANK YOU for the amazing tool that is Lit. It's everything you want from a framework without a framework getting in your way.
I'm sold and build all my work and personal apps with it and have for many years. I wrote this article about why in 2022:
Any whispers of something like lit being made part of the webcomponents standard?
Web components are nice because they're browser-native, but they don't support reactivity, which in hindisight, is a massive oversight, issue, whatever you want to call it - it's hindered adoption.
Lit is nice because there's a very straightforward progression from web components.
Lit has always been designed partially as a prototype for where web component standards could go in the future. That's a big reason Lit is fairly conservative and un-opinionated. It doesn't try to undo or paper-over any of the DOM APIs, but add to them instead.
There is a proposal in TC39 for native signals, which I think would make a huge dent towards library-less reactivity.
I'm also working on a proposal for native reactive templating which would more-or-less obsolete lit-html. I wrote about the idea some on my blog:
I hope there can be ways without JS to populate templates with data - autoloaded from sources. This would tremendously increase the number of JS free web-sites. Also wish the web-components standard did not mandate the use of JS. It should be possible to define simple web-components (HTML+CS) declaratively and have this officially supported in standard and implementation
I think we're stuck today in a little bit of a rut of extensibility. We wind up leaning on JavaScript to get things, because it is the Turning complete language in our environment. It is the only thing that can give us an answer when CSS and HTML fail us. So we wind up piling ourselves into the JavaScript boat. We keep piling into the JavaScript boat.
Bruce yesterday brought up the great example of an empty body tag, and sort of this pathological case of piling yourself into the JavaScript boat, where you wind up then having to go recreate all of the stuff that the browser was going to do more or less for you if you'd sent markup down the wire in order to get back to the same value that was going to be provided to you if you'd done it in markup. But you did it for a good reason. Gmail has an empty body tag, not because it's stupid. Gmail does that because that's how you can actually deliver the functionality of Gmail in a way that's both meaningful and reliable and maintainable. You wind up putting all of your bets, all of your eggs, into the JavaScript basket.
....
They're the sorts of things that you use when the semantic that you're trying to express is so far away from what HTML natively has a semantic for, that you're willing to go and be completely self served for it. Taking on for yourself accessibility, UI, UX, internationalization, localization, performance, theme ability; all of this stuff is mostly taken care of for you by HTML.
--- end quote ---
14 years later, web components depend on JS to:
- participate in forms
- solve accessibility issues (not yet, but coming through a yet another JS-only standard)
- CSS modules and other imports that are moved entirely into JS
etc. etc.
-----
Slight off topic. From the same talk:
--- start quote ---
Now, we can do some of that today. We can go and look at the top million pages and figure out which JavaScript library that they're using, which features are being most heavily used, we can put those things in DOM. But is that the right place for them? It's a hard thing to figure out.
--- end quote ---
I wish they spent time figuring that out. We wouldn't have to wait a few decades for https://open-ui.org to slowly make its way into the browsers.
The current draft is based on design input from the authors/maintainers of Angular, Bubble, Ember, FAST, MobX, Preact, Qwik, RxJS, Solid, Starbeam, Svelte, Vue, Wiz, and more…
I think web components already compete extremely well for application development, and you see very complex apps built with Lit out there: Photoshop, Firefox, Chrome OS, Chrome DevTools.
Apps are well served because they have more control about how components are used: they can import the same shared styles into every component, take are to not double-register elements, etc.
But I think there are some important standards still missing that would open things up even more in the design system and standalone components side:
- Scoped custom element registries. This moves away from a single global namespace of tag names. Seems like it's about to ship in Safari. Chrome next.
- Open styleable shadow roots. Would allow page styles to flow into shadow roots. This would make building components for use with existing stylesheets easier.
- CSS Modules. Import CSS into JS. Shipping in Chrome. About to land in Firefox.
- ARIA reference target: make idref-based reference work across shadow roots
What people using web components want is to get rid of shadowDOM and not feel like they are deviating from the correct path. shadowDOM sucks, stop trying to convince the world that we are using it wrong. shadowDOM is the whole reason web components did not become mainstream (yet?).
Nothing in Web Components is forcing you to use ShadowDOM. Lit also allows you to make components without ShadowDOM if you prefer, because there are certainly cases where it can be necessary to do so (like for ARIA reference id-matching). For full single application development, it can feel like it gets in the way a lot, and you can make a good argument to use components without ShadowDOM in those contexts too.
All frontend "frameworks" do have some sort of solution to scope CSS to individual components, and without a similar solution, a native component system would not be viable. The implementation has its quirks, but it is a core capability that is necessary for some use cases. For third-party widgets or cross-application components like design systems, the ability to isolate your component from the site it is embedded in is very useful.
Think of shadowDOM as the web component alternative to scoped styles in Vue components (as an example). You don't have to use it, but it would be incredibly inconvenient if it wasn't included in the framework.
> Nothing in Web Components is forcing you to use ShadowDOM
Yes. There is just one thing forcing someone to use shadowDOM: slots. You can't use slots without shadowDOM or at least use something like this.children to capture the content inside the <custom-element></custom-element>.
I disagree completely. Shadow DOM is a huge help and when combined with per-component CSS using the :host() and nesting pattern, makes for very small CSS files and very short CSS class names. In other words, as far away from Tailwind as you can get.
It's also possible to import shared CSS in a base class and add it with super.styles() so you don't lose anything.
You don't want to enable portable components all the time. If most of the components you use to create your app is made by yourself, those boundaries are annoying, not helpful.
Please do not refer to CSS type imports in JS as CSS Modules.
CSS Modules has an established meaning for over a decade, one that is still relevant today. The CSS type imports are very different, and arguably worse.
Call them CSSStyleSheet imports of you need a name suggestion.
Another pain in the ass is the fact web components are registered globally. Good luck marrying this with npm dependency hell where two transitive dependencies both import a button.
The good part of react and friends is it's just javascript and the class is imported and referenced normally, not with a weak string-binding-through-registry kind of way.
Now add types to the mix and shadow dom and it brings constant problems without any upside.
What I wonder is whether there are any developments related to Lit regarding server-side rendering (SSR). This is one of the reasons I'm experimenting with Svelte now. To me Lit was taking away frustrations with the inefficiencies of React. But for me Svelte seems to fill that gap too, and it also has SSR, among other niceties. But I do like the philosophy of Lit & Web Components. Sometimes I think a framework that would be like Svelte but uses Lit under the hood would seem like the best of both worlds.
I can't find clear information about how re-rendering and stateful third-party components interact.
Let's say I have a stateful data table web component that I use in the template. Is it going to be re-created every time the template is re-rendered (loosing its internal state)?
Lit's just a JavaScript library published as standard modules, so it doesn't require a bundler. Using Lit efficiently is the same as using any other library.
HTTP/3, import maps, and HTML preloads can make unbundled app deployment almost reasonable in some cases. You'd still miss out on minification and better compression form a bundle. Shared compression dictionaries will make this better though.
I use it in almost all my personal websites. And when I don't use it, I end up reinventing half of it and realize I should have used it from the start. This command is in most of my projects:
I've never felt I'm using a framework or anything that deviates from Vanilla JS and valid HTML, which is why using it hardly causes any more cognitive load than using regular string templates and JavaScript functions. Which is something that I can't say about other frontend tools.Another thing I like from Lit is that with the CDN bundle, it's straightforward to experiment and use all the features without needing a build step.