Description
LitElement 2.x has been out for about a year, and we’ve been considering what changes we should make for the next major version. We wanted to share our thoughts and get feedback from our users about potential improvements and breaking changes.
Overall, we’re generally pretty satisfied with the core functionality and API and we definitely want to keep in mind that dealing with breaking changes is a hassle for users. We do, however, think there are some good opportunities for improvement. The lit-html library is also considering changes for the next major version, and we’ll definitely be including the next version of lit-html with the next version of LitElement.
Goals
Backwards compatibility for most users
We know that dealing with breaking changes is annoying and we’re trying to carefully manage our change budget such that each change has either a big return on investment or is likely to affect very few users. If necessary, we may elect to create a compatibility version that maintains 2.x behavior if it makes sense.
Performance
We love performance and are always striving to improve it. The next version of lit-html has a number of performance improvements from which LitElement will naturally benefit.
Size reductions
There’s some good opportunity for improvement in the core size of LitElement. First, the next version of lit-html should be getting a good bit smaller. Then, there’s a good bit of refactoring we can do to help make sure users are only getting the code that they use, including making old browser support opt-in, making decorators opt-in and more granular, and streamlining some of our internal API.
In addition, we may publish a minified, bundled build so that we can ensure that a heavily minified version works correctly. While we believe minification is ideally an app concern, we realize that apps won't know all the transforms that can be safely applied to our code.
Developer Experience
We want to make sure LitElement is easy to use for all our users. We know that some web devs love the pure experience of downloading some code, editing in a text editor, and seeing the output in a browser and we want to make sure that we support that use case better.
We would like to improve error messages, and not let publishing minified code negatively impact development. We'll look into producing both production and development builds.
Easier SSR
We know users want SSR, both for SEO and first paint performance. By leveraging the support for SSR being built into the next version of lit-html, LitElement will support SSR and it will be able to take advantage of the declarative Shadow DOM feature being added to Chrome.
New Features
While we want to consider some feature additions, we’re pretty happy with the core functionality. One thing we’d like to explore is providing a package of helpers for features not everyone needs but are really helpful for specific use cases or are just one way to skin the cat.
Fix bugs that require minor breaking changes
We have a number of longstanding minor issues that we haven’t been able to fix because they are just slightly breaking changes. We’d like to evaluate those and address what we can.
API cleanup
We think there’s a number of opportunities to reduce code size by carefully sweeping through the API. This should mostly be internal changes, but there may be some minor changes to the public facing API where we think there’s a strong benefit.
Potential Changes
Opt-in polyfill support
Although the Shady DOM polyfill itself is opt-in, there is some code baked into LitElement to use the polyfill that all users must pay for, which adds close to 1KB (~15%) to the bundle size. We’d like to make this opt-in so users who don’t need to support older browsers don’t pay the cost of downloading the polyfill specific code.
Opt-in and granular decorators
LitElement's main module exports all of the decorators we support regardless of whether or not they are used. We will break these out into granular modules that users will import if they use. This will also allow us to more freely add new decorators where useful.
Package that works out of the box without building
For optimal code size a build will always be best, but we want to provide an easy way to try LitElement without using npm and building the code.
Guidance for use in popular frameworks
The main value proposition of web components is that they work everywhere the (modern) web works. That’s true but they should also work seamlessly with modern web frameworks as well. We want to make sure LitElements work great in popular frameworks like React, Vue, and Angular. As shown on custom elements everywhere, the situation today is pretty good, and we may only need to create a set of examples showing what’s possible. However, we will be considering patterns and helpers that can make things easier for framework users.
Code sharing primitive
We think that subclassing and mixins cover most of the cases where users want to share code between elements. However, there are some cases they don’t cover. We’ve been impressed by efforts in the community to address those use cases, in particular React Hooks and the Vue composition API. We think there’s an opportunity to do something similar but probably a lot simpler for LitElement. This might take the form of controllers that can bundle functionality via a has-a relationship with the element and that can interact with the lifecycle of LitElement. Since not everyone needs this feature, this is probably a good candidate for a helper package.
SSR Support
When SSR output is being generated we need to consider which parts of the element lifecycle to run. It’s a goal of our SSR implementation not to require a server side DOM implementation and therefore code that uses DOM APIs will have no or very limited support. We will likely be modifying the update cycle for the element when it’s run on the server to avoid methods which often perform DOM manipulation, for example the firstUpdated
and updated
methods.
Theming
Platform support for theming is still incomplete. CSS custom properties are great for tree-based theming of known sets of properties. The new ::part
selector works well for arbitrary theming of specific elements. However, the exportparts
attribute is a cumbersome way to expose parts up the tree. In addition, it’s often desirable to allow users to style a set of unknown properties but not everything. There are a lot of patterns that can help here and we may end up supporting a few of them, perhaps in a helper package. We like the Vaadin themable-mixin and something like that for LitElement may make sense. We also like the power and configurability of Sass mixins and may explore exposing something similar via a Javascript API.
Declarative events
Declaring what events your element fires is great for self-documenting. We can also follow the DOM convention of providing an on
prefixed property to listen to the event. Having on
prefixed properties also improves integration with frameworks like React that configure events this way. We will probably implement this as a decorator but will have some form of raw JS support as well. See this issue for more info.
Common customization like shadowRoot rendering
To customize how the element’s shadowRoot is created you currently have to override createRenderRoot
. We’d like to make this a bit easier and may add a decorator or static field with which you can supply the options object to attachShadow. For example:
static shadowRootOptions = {mode: ‘open’, delegatesFocus: true}
We may also explore this for other common but simple to configure customization points.
Declarative host manipulation
While we use lit-html to render the element’s Shadow DOM, attributes and event listeners on the hosting element itself must be added imperatively. We may be able to use some of the new features in lit-html 2.x to create a declarative way of doing this. This might take the form of a renderHost
method which returns a lit-html TemplateResult that’s rendered on the host element. See this issue for more info.
Update coordination
LitElement updates asynchronously at microtask timing, and while this can’t be observed by the user, it can be by code. After an element has been interacted with, it’s sometimes useful to know when it’s finished rendering; for example, this is useful in testing, when firing events, or when you need to know the element’s rendering is stable. Although this can also be used for measuring DOM, it’s usually better to measure in a requestAnimationFrame
callback. LitElement provides the updateComplete
promise for these use cases. However, this only covers the element itself and not any LitElements in its shadowRoot. To wait for those elements, typically you override the updateComplete property and await those elements as well. That’s a bit cumbersome, and we want to explore adding a helper that makes this easier. Since this is only sometimes needed, this might be another feature which gets added to a helper package.