Lit: Simple, fast web components
lit.dev
lit.dev
A few of the unique things I try to emphasize when talking about Lit because it's so different from a framework:
Lit is just helpers to make web components. It's implementation detail. On the outside the components are just standard HTML elements.
Because of that, Lit makes no assumption that other elements you're using in your components are made with Lit. This low coupling preserves interop, and makes it easy to incrementally adopt Lit, and incrementally leave it if you wish. Low lock-in is an important principle for us.
Because of the low coupling, Lit doesn't have any centralized scheduling, diffing, etc. Each component is its own independent render root and schedules its own updates in microtasks. This has some great upsides - it's very easy to modify scheduling per component, and decouple costly subsections of a page to limit jank.
And we get great performance from the emergent global properties of independent roots. We check for data changes in the property setters for each component, so data updates only cause component updates along the paths in the tree that both use that data and see a change. Then lit-html, the template system, doesn't use VDOM, but remembers where data is bound to the DOM and only updates that if the data changed. It's very efficient.
Also, Lit requires no compiler to toolchain. You can use it from a CDN, or with import maps, or with a tool that resolves JS modules import specifiers. You can use decorators with TypeScript, or not. You can bundle or not. We do have TypeScript and ESLint plugins for working with templates.
Looking forward to using Lit in the future since I think it does a really great job of getting out of your way and being supremely easy to use and standards-adjacent.
> Because of the low coupling, Lit doesn't have any centralized scheduling, diffing, etc. Each component is its own independent render root and schedules its own updates in microtasks. This has some great upsides - it's very easy to modify scheduling per component, and decouple costly subsections of a page to limit jank.
Could you share some downsides?
> Also, Lit requires no compiler to toolchain. You can use it from a CDN, or with import maps, or with a tool that resolves JS modules import specifiers. You can use decorators with TypeScript, or not. You can bundle or not. We do have TypeScript and ESLint plugins for working with templates.
This is awesome -- thanks for
As for an actual new question, I'd love to know if there are any considerations on shared data in Lit -- with the just-web-components nature of Lit, data & reactivity are hard to fit in obviously but would be very beneficial to solve in a way that is just as easy to use as the rest of lit.
I took a stab at what it could look like and wrote a demo[0] and post[1], but have not kept up with the Lit community since then to see if there's some other solution/pattern gaining steam.
[0]: https://mrman.gitlab.io/services-as-dom-elements
[1]: https://vadosware.io/post/sade-pattern-services-as-dom-eleme...
https://github.com/alexanderweiss/mobx-lit-element
and (documented as based on the former)
https://github.com/adobe/lit-mobx
already out there, and next time I get some Copious Free Time I plan to experiment with adobe's connector plus mobx-state-tree (https://mobx-state-tree.js.org/intro/welcome).
NB: I haven't used the above for anything 'real' yet ... but I do think at the very least it's worth a look to steal ideas from.
Feels like almost all of the world is now wonderfully DOM-friendly/not hidden from HTML but maybe it just doesn't make sense to try to make state declarative in terms of the display layer anyway.
It sometimes makes working with _slotted_ children in a non-lock-in way difficult in terms of state synchronization as opposed to props.children in React. The solution here in an app would be to use a state manager, but it's much more difficult for components that do not want to require the user to use a state manager e.g. design systems.
Additionally, hydration and customElements.define() upgrade order is a different consideration compared to other mono-state frameworks when it comes to SSR.
In terms of state management:
There are plenty of tools out there like lit-mobx shopped by Adobe. The team has also made some examples that show how easy it is to integrate your own state manager into Lit using ReactiveController such as Redux. There is also work being done such as example implementations of Preact Signals integration:
https://github.com/lit/rfcs/blob/preact-signals/rfcs/NNNN-pr...
I'm just thinking aloud here based on your description (and note that I'm a backend/data engineer looking to build data apps with no front end experience), would this integrate well with htmx then since each component is independent?
I really like the htmx approach and have been looking for a good visualization component framework that will play nice with it.
I'm sure there is a better way to do it but (10 seconds of thinking time) maybe a top component with just an empty div and a reactive property with the global state and the code to apply changes to the reactive properties of the appropriate children components, no matter how far down the tree? That seems something to standardize and not code from scratch every time.
It utilizes lit-html's compiled template support to transform HTML templates into lit-html templates and reuse the underlying efficient rendering / updating machinery.
Is it maybe no longer associated?
The thing I like most about lit is that it embraces web standards. Sometimes that means the ergonomics are a bit strange. But it also means it gets performance benefits, and interoperability bonuses. Plus it feels like you’re learning something about the underlying platform while you’re using it.
The best example is that it uses reactive data binding via attributes for passing data down the tree, and native DOM events for passing data up the tree. That allows you all the same safety that a framework like React offers, but also means your component can be used by any other framework, or by vanilla JS (because they all support the DOM).
What I really want is a good set of unstyled web components implementing many of the common UI controls that we all end up either building ourselves or finding an implementation of in our framework of choice. Things like drop down menus, select boxes, and tool bars. They could then be used with any of the common front end toolkits.
They are 50% of the way to building a universal mobile+ desktop ui toolkit.
Do the same with Ionic Capacitor, make a desktop version, and that would be an incredible improvement over Electron - it uses the OS supplied web view.
The whole shadcn/tailwind philosophy of having you own your components is the future.
I'm not really sure what you mean by "own your own components"?
It's a webcomponent that gracefully degrades to es5 + polyfills for IE (this was years ago).
Getting the rollup build to work correctly was a bitch and a half, but it turned out nice and it's still in use today.
That said, I commonly find that most folks don’t want unstyled components because it’s quite a bit of work to style every single state of every single component. Most people just won’t do it or, if they try, they don’t do it right and they don’t do it completely.
What many folks do want are components that have simple, generic styles you can quickly tweak to match your site/app using a skill you already know: CSS. (This is something you can do with Shoelace’s 50+ web components already, but we’re working to make it even easier with additional CSS custom properties.)
Can I ask, are ARIA attributes handled by default?
> My commitment to Shoelace users is this: Everything I develop will be built with accessibility in mind. I will test and improve every component to the best of my ability and knowledge. I will work around upstream issues, such as browser bugs and limitations, to the best of my ability and within reason.
Thank you very much.
I'm very happy with it. I don't have a use-case for web components (so far) and am happy to just wire up dom events to state changes to lit-html. It requires _very_ minimal plumbing and is very easy to reason about.
One of my complaints with Lit and others[0] is they're most definitely becoming something of an "Angular Lite". Heavy on decorators if you want developer ergonomics, but yet they embrace none of the advanced toolchain things you get with angular-cli. Due to this, it can feel very clunky to build apps using web component frameworks like this. The community as a whole seems very anti-tooling and I think its to the long term detriment of the ecosystem.
The only exception is Stencil[1] that I can find.
They're also missing first class concerns you get handled with other frameworks, like SSR, compiled templates etc. There's no web component equivalent to Next.js that I am aware of.
I don't like using them for design systems. You bail on your framework of choice rendering model, and that's problematic to me.
Are decorators the only thing making Lit like Angular? Angular is not the only project that uses decorators, and decorators are standardized in JS now.
Stencil is good, but requires a compiler where Lit doesn't.
What tooling are you looking for? We have template type checkers and eslint plugins. Starter kits. Prettier works for formatting...
Decorators are not the only thing that make it like Angular. The way templates work, template helpers (directives), controllers are all very Angular like in practice.
Tooling is much more than linters, eslint plugins and starter kits.
Unit & e2e testing helpers, build tools, HMR / dev server support. These all matter too and its important that they keep up to date with expectations of developers in what they expect out of these experiences. Even better when it is all rolled up in a nice CLI interface.
Not to mention it would be nice to have optimized convenience features, like Vue style SFCs for authoring components and templates.
Stencil requiring a compiler is not a negative. It makes it an end to end solution and thats very popular for a reason (Next.js, Nuxt, Sveltekit are all very popular for a reason).
I believe strongly that projects need to provide these things as 1 party concerns. I think the Vue community is proof positive of how strong this can be for the ecosystem.
I'm certain between Google, Microsoft, Ionic, and many others there could be real solutions for all this. After all, the biggest advantage of using web components is they are a shared interface
Lit templates and massively different from Angular templates, and more like React templates if anything. All Lit control flow and expressions are just JavaScript in Lit, like React, and different from Angular.
Directives in Lit are just JavaScript function calls you use in bindings. In Angular they are things that get access to the template and contents themselves.
Angular doesn't have a concept similar to Reactive Controllers that I know of. They are more similar to custom React hooks.
Stencil was the cleanest to me because components felt like components, and could be interacted with more naturally.
There's just alot of missing pieces. Tooling matters. Ecosystem matters. Developer Experience matters. There's a reason so many people turn to Next.js or Nuxt etc.
If anything lit has some additional niceties, a better ecosystem, and less restrictions that gets imposed by stencil's compiler
I still don't understand the criticism. Lit template have HTML elements, bindings, and control flow. They're pretty JSX-like just within standard JS syntax. Stencil is the same, but JSX instead of HTML strings.
Do you have any specifics on what the extra boilerplate is to you? What makes Stencil components feel like "components"? Are you just saying you like JSX?
I want to feel like I'm working with native browser features as much as possible. Personally, I would even consider doing a project with plain HTMLElement. If I found that I absolutely needed the convenience of state reactivity, only then I would use Lit.
At the heart of Lit are components named "html" and "css".
Confusion reigns, search engines cower, beginners try to grasp the conceptual different between html and actual html and css and actual css. You'd think the authors would have gone to the trouble of naming those critical components something like lithtml and litcss to give them a little searchability and to deconfuse them, but it seems not.
https://lit.dev/docs/components/overview/
I agree that those are odd choices and the arguments provided seem reasonable I.e. they’re likely ungoogleable, and beginners would be confused.
Those names are well supported by editors and enable syntax highlighting. If we named them anything else it wouldn't work without extras tools.
One of my primary criteria in choosing a technology to work with its how accessible is community support, documentation, third party articles and writing.
It had better be a compelling technology that names its main things exactly the same as the biggest concepts in web development, thereby making it a battle to resolve questions and issues and adding unnecessary confusion.
I do use a nodejs library called "Postgres" which provides a Postgres SQL driver but I really hate the fact that searching for documentation/issues is essentially impossible. I use it because I feel I had no choice, it's the best possible technical solution.
Names matter.
Searchability matters a huge amount.
Template literals are just a feature of javascript, it's not even anything that bespoke. That's why it's called "lit". The output of those tag functions are just rendered with standard DOM features too.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Did you see a code snippet and want to learn more?
`html` and `css` are just functions we vend. They're important ones, but two among many. The names need to be short and intuitive so that when you see something like:
html`<p>Hello world</p>`
both the developer and tools know what to expect of the string content. Naming them something unique would really harm readability, IMO.It's not, and hasn't been for a long time. It's its own DSL with unexpected constraints on what you can use with it.
`<some-component
attribute="string only"
?boolean=${boolean}
.property=${any_js_variable}
@event_name=${callback_function}>
none of this is "just html"
</some-component>
`Server-side rendering is still a missing piece. But overall, I'm very happy using it going forward.
And if you need a web component library to pair it with, Shoelace[0] is a fantastic library of prebuilt web components using Lit. I'm loving this stack.
If you want to drop in a calendar component into your page from npm, it helps if that component's styles don't leak into the page, and if page scripts don't accidentally mess with component internals.
This seems overstated. It’s different but not really harder.
document.querySelector('my-lit-element').elementIWant;
class MyLitElement extends Lit {
get elementIWant() {
return this.renderRoot.querySelector(...);
}
}
Though, maybe one doesn’t like querying twice. I’m not sure of performance implications there, to be honest, but I’d expect it to be negligible in most cases.They don't work as well for building apps, sure you can do it, but tools like React/Vue/Svelte are much better at building a whole app experience. Particularly when you consider meta-frameworks and the ecosystem around them.
They could work well for building apps if the ecosystem was more invested in giving developers what they actually want in a timely manner. (Looking at you template instantiation)
The problem with using them for component libraries is that you break your rendering paradigm of the framework you're using, and that can lead to situations like trying to add event handlers to child components impossible for instance, or having to over-use refs
The language is still there. If anything, it has become more aggressive. Now lit (and stencil) are "standard-based" and "future-proof", and "they are not frameworks", unlike all those non-standards-based framework abominations. (at this point most people roll their eyes).
> They could work well for building apps if the ecosystem was more invested in giving developers what they actually want in a timely manner.
The problem is that none of the people involved in the development of web components ask what developers want. If they ever ask, any answers (including answers from other framework authors and contributors) are ignored, derided, and misconstrued.
There's a reason even people and frameworks who really advocated for web components in the very beginning (Vue, Svelte, Solid) are completely against them now.
Doesn't mean it's a good feature. This is recognised even by people who push them: https://w3c.github.io/webcomponents-cg/2022.html
--- start quote ---
It's worth noting that many of these pain points are directly related to Shadow DOM's encapsulation. While there are many benefits to some types of widely shared components to strong encapsulation, the friction of strong encapsulation has prevented most developers from adopting Shadow DOM, to the point of there being alternate proposals for style scoping that don't use Shadow DOM. We urge browser vendors to recognize these barriers and work to make Shadow DOM more usable by more developers.
--- end quote ---
We pair it with Storybook, finicky though it is, to have a catalog of our components and stories showing off the variances for each.
https://docs.pwabuilder.com/#/starter/adding-content?id=addi...
// https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements
// Create a class for the element
class LikeButton extends HTMLElement {
static get observedAttributes() { return ['liked']; }
constructor() {
// Always call super first in constructor
super();
this.liked = false;
// Create a shadow root
/* const shadow = */ this.attachShadow({mode: 'open'});
this.render();
}
get liked() { return this.hasAttribute('liked'); }
set liked(state) { this.toggleAttribute('liked', Boolean(state)); }
attributeChangedCallback(name, oldValue, newValue) {
this.render();
}
render() {
const shadow = this.shadowRoot;
if (this.liked) {
shadow.innerHTML = 'You liked this.'
return;
}
shadow.innerHTML = `<button onclick="this.parentNode.host.liked = true;">
Like
</button>`;
}
}
// Define the new element
customElements.define('like-button', LikeButton);Lit's just there to help once you handle more features and corner cases, and do this for 10's to 100's of components.
We're hear numerous times from developers who start with raw web components that the started to put common functionality into a base class, expanded their base class, then realized they were building a clone of LitElement, so switched to Lit.
That's fine too! Leaning on common, well-testing implementation is great, and being able to switch to it seamlessly is one of the benefits of web components.
@customElement('like-button')
class LikeButton extends LitElement {
@property()
liked = false;
render() {
return this.liked
? 'You liked this.'
: html`<button @click=${() => this.liked = true}>Like</button>`;
}
} customElements.define('like-button', class extends HTMLElement {
static get observedAttributes() { return ['liked'] }
get liked() { return this.hasAttribute('liked') }
set liked(state) { this.toggleAttribute('liked', state) }
constructor() {
super().attachShadow({ mode:'open' });
}
attributeChangedCallback() {
this.connectedCallback();
}
connectedCallback(){
this.onclick = (evt) => this.liked = !this.liked;
this.shadowRoot.innerHTML = this.liked ? '<b>You liked this!</b>' : `<button>Like</button>`;
}
});
Now, a shadowRoot is a bit wasteful here, as inheritable styles *will* style shadowDOM.So we remove shadowDOM:
customElements.define('l1ike-button', class extends HTMLElement {
static get observedAttributes() { return ['liked'] }
get liked() { return this.hasAttribute('liked') }
set liked(state) { this.toggleAttribute('liked', state) }
attributeChangedCallback() {
this.connectedCallback();
}
connectedCallback(){
this.onclick = (evt) => this.liked = !this.liked;
this.innerHTML = this.liked ? '<b>You liked this!</b>' : `<button>Like</button>`;
}
});
To do that in Lit, you actually have to *ADD CODE* createRenderRoot() {
return this;
}
Making the Lit code LONGER than the Native code import {html, css, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
@customElement('like-button')
class LikeButton extends LitElement {
@property({type: Boolean, reflect: true})
liked = false;
render() {
return this.liked ? 'You liked this.' : html`<button @click=${() => this.liked = true}>Like</button>`;
}
createRenderRoot(){
return this;
}
}
In real live projects you won't be nitpicking about these bytes and the 6K library/BaseClass Lit adds. Or the 7K lit-element.Or would you? When those bytes are added for each! component if you develop truly self-contained web components...
Most Web Component Developers are still building Apps _with_ Components, not Apps _made of_ Components.
When doing Native you will *ofcourse* develop your own *BaseClass* (like Lit is) And for 95% of your time you will just be doing Plain Old JavaScript code.
Most Litters don't have a clue what is going on under the hood.
Most Native developers just silently do everything native, they are not the type to evangelize their choice on Social Media. Their code will run without any issues, upgrades, or breakin changes, for the next 25 JavaScript years
LitState: Shared component state management - https://github.com/gitaarik/lit-state
LitStyle: Shared component styles - https://github.com/gitaarik/lit-style
LitDocumentEvent: Global event handling - https://github.com/gitaarik/lit-document-event
Also check out "Lion" from ING, which contains all kinds of handy components that you can use and configure and style to use them in your app: https://github.com/ing-bank/lion
a) Select dropdowns as feature-rich as select2 (search, style, custom click handlers, custom appearance in box, multiselect, callbacks)
b) Data Tables as feature-rich as jQuery DataTables (search, sort, paginate, w/ server-side data, callbacks)
c) A high-quality, customizable charting library that's interoperable with the components
d) A slider / carousel / swiper that's as feature-rich as swiper.js
e) A sane and typical component library (ideally from one place like Ant)
I don't build libraries, I want to use them to build applications, and I don't want to spend too much time re-inventing them or making them play nice with each other - i.e. figuring out when one of them is done initializing so that I can trigger the "initialize" of the other and so on.
The solutions thus far range from some compilation hellscape where I have to write everything in a custom dialect, which then gets compiled to HTML/CSS/JS and then I have to debug things cryptically because it's generated code.
The other end of the spectrum is something that's lightweight and uses the platform extensively like htmx or what lit appears to be positioned as.
I've written production applications using Angular, React, Vue, Alpine and it seems like the newer the library, the less likely it is to have an easy way to support the large selection of component libraries you could just throw together and they "just worked" in the days of jQuery + something. As a result, I end up having pages that are still jQuery + something because neither my users nor my customers care about these details, and the tradeoff is minor.
As a developer it sure would be nice to have a dx where you didn't have to reinvent these things or work with ancient libraries.
The flip side is that you should ideally be able to find pre-made web components that fit your need, like I think you're asking for. There are a lot of components out there, which you can hopefully find with enough diligence. What's missing is a great comprehensive catalog of components. Something I've tried to work on, but it's hard to find enough time or funding to complete.
They also sometimes sit abandoned and unmaintained.
Some catalogs use ratings/votes but they aren't a good heuristic to factor in these two details.
I'm more than happy writing vanilla JS for my own event handlers and components. I don't mind adding a small library like alpine either to make the click handlers etc easier to write.
Probably something to take up with ChatGPT at some point.
One thing I don't understand in lit (or vuejs, svelte etc...) is why we can't have an each loop that would use information about what has changed.
In my most common case, I am simply updating an item in an array (or removing/adding an item) and the each loop could use the list of operations to do the changes instead of iterating over all the items.
For large array (1000+ items) for a SVG diagramming tool, performance would be much improved, no?
SSR support is coming along. It's taking a while because we're trying to do it in an interoperable way, both to allow mixing of web components from different libraries, and to plug into framework-specific SSR (There are Lit SSR plugins for Next and Nuxt available).
https://webcomponents.dev/blog/all-the-ways-to-make-a-web-co...
Which puts 61 of web-component libraries side-by-side. Sadly my favorite, RiotJS, is not on the list.
Looks like it might be in the "Wrapped into a custom-element" category.
I highly endorse this podcast, but I have not listened to this one yet.
> defaulting to deprecated API documentation
I am not sure what point is being made here. This seems to be splitting hairs.
Regardless, it makes sense to default to the docs for the version which is considered to be officially released. That version is probably most likely to be used in a production application, which is usually what the docs are intended to support.
I can see that, the website re-renders only three times on load (text - image - fonts).
Just use Angular or React or one of whatever the top 3 things are right now.
I would say starting a new application in 2023 based upon Angular or React would be the Ooof thing to do.
Such a joy.
I'm using web components in production to help interoperability between 2012 era Handlebars/jQuery/Backbone and modern tools like React and Prosemirror.
Reading between the lines here I'm getting the feel that this is actually, "Web Components work in every browser that people use to spend money." And I don't disagree. If you're doing something for work and being paid to do it then by all means use web components. The large number of people around the world you exclude weren't part of the demographic you were going to get money from.
But saying that it works in every browser that matters is wrong.
I think "every browser that matters" means, yes they don't work in IE or legacy Edge, and maybe don't work in Servo or Ladybird, but they work in Firefox, Edge, Chrome, Safari, Opera, etc.
I’m curious: does it work in Lynx[0]? I assume no because there’s no executed javascript, which is required to register the component as far as I know.
It’s worth stating the point in a different way. Even one person using such a browser “matters”, in a way, and it’s good to be kind when one can.
(Apologies if this comes off as harsh; however, it does seem to me that the other commenter has a point, at least in their second comment.)
0: https://en.wikipedia.org/wiki/Lynx_(web_browser)
----
“Virtually no one” does sound a lot like someone. Per this point of “browsers that matter”, many people will also say “accessibility matters”. Support of Lynx is a worthwhile goal in regard to web accessibility, in no small part because it does not execute javascript.
Note that Safari has opposed subclassing built-in components since the very beginning: https://github.com/WICG/webcomponents/issues/509#issuecommen...
Just a few of the reasons: https://github.com/WICG/webcomponents/issues/509#issuecommen...
However, there are now hundreds of millions of dollars of sunk costs, dozens and dozens of specs, unbelievable complexity that infects all other actually useful specs (like Scoped CSS which cannot proceed properly because effing Shadow DOM), extreme zealotry and complete unwillingness to engage with anyone even mildly critical of web components.
All this results in a strong desire to keep going and promoting this even if no one can even say what the "done" state is for them. Or what is the actual goal, since that goal changes every few months.
[1] https://twitter.com/Rich_Harris/status/1198332398561353728 None of these have any satisfactory solution (for some not even on the horizon)
[2] Even the people pushing this stuff realise how many issues they have: https://w3c.github.io/webcomponents-cg/2022.html See how many of those are not even close to even being discussed
General browser support is good though. Debugging ShadyCSS in IE11 was a nightmare.