Closed sorvell closed 2 years ago
Excited for this. A couple things that came to my mind:
updated
callback, but I still find the issue I raised there to be worthwhile fixing and now might be the time. updated
extensively, I like it, but I feel some of the user experience could be improved:
updated
. Worse even there would be no (compile) error, so this would only ever be noticed when the component is in use or testing. Oooo, lots of good stuff to chew on here… I'm particularly interested in the "Code sharing primitive" angle, as it something I was thinking about recently as well. I did a lot of StimulusJS work before becoming enamored with LitElement, and one advantage Stimulus has is you can attach multiple controllers to a single element. It would be cool if I could explore an analogous pattern in the LitElement world…
@Christian24 lit/lit-element#469 is already addressed in 3.0. requestUpdate()
does not return a Promise anymore, and you have to specifically call await this.updateComplete
.
may we have good documentation on bundling large apps, and coding exercises / challenges.
A functional approach such as react hooks would really be nice.
may we have good documentation on bundling large apps, and coding exercises / challenges.
I think that's already taken care of by open-wc?
How early do you think we could start to see tech specs on the controller proposal mentioned herein? It sounds like a great tool for empowering both early LitElement learning as well as advanced/shared application development. I’d love to see whether you’d see it deeply integrated with LitElement, a pair to lit-html, or something more abstractly applicable to UI development at large.
@funglaub
A functional approach such as react hooks would really be nice.
We feel that we already have a pretty functional approach with the render()
method of LitElement.
There's a lot of dislike of classes out there, but for this case where we 1) are creating objects by nature of the DOM and 2) do need to declare properties and attributes, classes are actually more declarative than a "pure" functional API where attributes and properties must be declared without the advantage of native syntax for it.
@Westbrook
How early do you think we could start to see tech specs on the controller proposal mentioned herein?
I'll try to publish an issue today.
Exciting ! :)
On Fri, Sep 11, 2020, 11:28 AM Justin Fagnani notifications@github.com wrote:
@Westbrook https://github.com/Westbrook
How early do you think we could start to see tech specs on the controller proposal mentioned herein?
I'll try to publish an issue today.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Polymer/lit-element/issues/1077#issuecomment-691162521, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFSDL2W6OPJSKVVVSCGAXI3SFI62JANCNFSM4RDTWGKQ .
I really like LitElement, so thank you in advance!
I think it would be awesome to see like a showcase of who else/what companies are also using it as this would be great publicity and help advocate its usage in organisations.
I think more demos/starter kits, already mentioned is how to use it with other frameworks but I think examples of large applications/advanced usage would be great and also starter kits that can generate the boilerplate for standard usages e.g. with an app shell, routing, linting, testing etc. Would be happy to contribute to these!
Replace html templates with compiled js. Like Svelte and solid-js
@kmmbvnr
Replace html templates with compiled js. Like Svelte and solid-js
Any reason why?
It would be great to get support for <slot>
or an equivalent for the light dom. If I'm not mistaken, Stencil supports this. The thing is, you don't always need or want shadow dom, but the concept of slots is still very handy.
It could be nice to streamline this kind of pattern with decorator options
class WaitsForChildren extends LitElement {
render() {
return html`
<x-l id="a"></x-l>
<x-l id="b"></x-l>
`;
}
async _getUpdateComplete() {
await super._getUpdateComplete();
await Promise.all(Array.from(this.shadowRoot.querySelectorAll('x-l'), x => x.updateComplete);
}
}
class WaitsForChildren extends LitElement {
@query('#a', { awaitUpdate: true }) a: XL;
@query('#b', { awaitUpdate: true }) b: XL;
render() {
return html`
<x-l id="a"></x-l>
<x-l id="b"></x-l>
`;
}
}
class CustomInput extends LitMixin(HTMLInputElement) { ... }
// or
@builtIn(HTMLInputElement)
class CustomInput extends LitElement { ... }
// or any other solution to reconstruct the prototype chain
One thing I've been running into a bunch lately is when I have a Lit component that requires me to know my own component bounds as part of the render (I'm absolutely positioning elements), there's no great update mechanism. In fact it was kind of terrible previously, but at least now I can listen to ResizeObserver. It might be nice to have some set of bindable properties that the user doesn't change, but they can opt into that trigger requestUpdate
.
For example:
@property({ type: Number, internal: 'bounds.width }) protected componentWidth: number;
Bounds of course would be from this.getBoundingClientRect
.
I'm not married to the my implementation suggestion by any means, but all the same, if it was similarly implemented, it could open the door to anything that you might add-on here if there was an API to do so (timers, sensors, data request results, etc)
Honestly, this type of functionality probably doesn't have to be part of Lit, but I'm kinda sick of re-writing ways to handle this. So if not Lit core, some kind of companion util might nice
@bengfarrell I think this might be nicely addressed with what I've been calling recently "reactive controllers" - helper objects that are able to hook and trigger the host element's lifecycle.
Imagine a controller called BoundsController
which installs a ResizeObserver and on resize pulls the bounding rect and triggers an update. It'd be useable like:
class MyElement extends LitElement {
private _bounds = new BoundsController(this);
render() {
return html`<pre>width: ${this._bounds.width}, height: ${this._bounds.height}</pre>`
}
}
Note, this is pretty easily writable today - it doesn't require any new APIs on LitElement.
@justinfagnani I think that's exactly the type of thing I was steering towards if I put more thought into it. And yes, it IS easily writeable today, but as a user the design pattern didn't really occur to me until I thought about what I was missing. And this is why I'm wondering if a few reactive controllers could be shipped alongside Lit (just import when you need it, not part of the core lib), in the same way you're offering extra directives. The few that ship could solve some common problems like bounds, but more importantly offer guidance to create your own. Of course, this might fall under the category of "too opinionated" a design pattern to ship with Lit
@justinfagnani CustomElements spec allow you to register a component only once which is very much sensible but it would be great if the decorator @customElement('my-element')
can skip declaration if it is already in the registry instead of failing with an error.
Currently I am doing customElements.get('my-element') || customElements.define('my-element', MyElement);
to support this.
This is relevant because I use storybook for demoing and strangely, when you include same components in multiple different stories, they get registered multiple times throwing errors in the storybook.
A similar issue in a diffent context was raised here I guess: https://github.com/Polymer/lit-element/issues/207
Another thing which may not be relevant to this context is that, while everything is awesome, VSCode support is limiting. There are 1 or 2 extensions but they don't support everything like css language support within the css
tags (or am I doing something wrong?)
Also, there is no syntax highlighting in this case 😢
Infact, I am even wondering if it is recommended to put the .css
file separately or write css within the css tags.
PS: The <style>
tag within html works well though but I just use html tag for html and css tag for css
Also, for people who are starting off something which you guys did like PWA helpers (https://github.com/Polymer/pwa-helpers) for lit would be great. While open-wc has https://open-wc.org/developing/lit-helpers.html it may either need more additions to it or you may want to take it up. For eg. the recommended way to do routing (with each element handling its own routing) would be great. But, this is not a must have and not related to lit 3.0 either, just a suggestion.
@tvvignesh
@customElement('my-element')
can skip declaration if it is already in the registry instead of failing with an error.
We'd rather rely on a standard solution to this like Scoped CustomElementRegistry. Protecting against this exception can mask true errors in a program. We don't think that first-to-register is a reliable way to handle conflicts.
I also wouldn't want to make @customElement
harder to explain. There's a big benefit to saying that it's just a declarative form of customElements.define()
and behaves the same.
Re: VS Code plugins, I get CSS highlighting with lit-plugin: https://marketplace.visualstudio.com/items?itemName=runem.lit-plugin I'm not sure what's happening for you, but I'd use that or file an issue if you already are.
Re: App recommendations. We're definitely looking into addressing these topics directly in docs. I'm not sure we'll call them recommendations or not, but we do want to guide developers towards answers for common needs like routing.
Hello all !
Some really great stuff going on in here - I wanted to post a snippet of some code which aims to give inspiration as to how we use controllers and lit-element within Runtime.dev today in our products - which is built on the component controller model as well as on top of LitElement. I also wanted to share some thoughts on the aforementioned topics!
Runtime.dev's component lib is built on a component controller model and seeks to achieve some of the things that are being noted here, Injectable styles, differential rendering, separation of state/logic and markup, both DOM and JS event dispatching & execution management, graph-linked/connected controllers, dynamic accessibility & focus management through controllers, etc. The list goes on.
protected description = {
// Icon Controller Used in coordination with the input's validation state
icon: new IconController({
// Initial Icon SVGTemplate
svg: CIRCLE_ICON,
// Standardized rem sizing - can use IconSizeEnum.INHERIT to style externally
size: IconSizeEnum.SMALL,
accessible: true,
aria: {
label: 'Description Completed Icon',
role: AriaRoleEnum.PRESENTATION
}
}),
input: new InputController({
// Differential Theme
theme: DefaultTheme,
// Initial States of input validation
valid: false,
// Fully async Validation Function
validate: async ({ value }) => {
const valid = Boolean(value);
// Updating icon of other controllers when validated state changes
this.description.icon.svg = valid ? CHECK_ICON : CIRCLE_ICON;
return {
valid,
error: {
message: '',
caught: false
}
}
},
placeholder: 'A description also...',
// Injectable templates that get injected into shadowRoot as opposed to slots
trailTemplateFn: ({ valid }) => html`<runtime-icon .controller=${this.description.icon} class=${valid ? 'valid' : 'invalid'}></runtime-icon>`,
// Injectable Styles which apply to injected content
styles: [css`
runtime-icon.invalid {
${svgPrimaryColorLightest};
}
.valid {
${svgPrimaryColor};
}
`],
//JS Tied in event callback
engagedCb: async (e) => console.log(e)
}),
};
get details() {
const { information: { card, descriptor }, name, description, } = this;
//Inject all controllers into markup
//Components handle Animation, Interaction, Layout, and Styles
return html`
<runtime-card marginTop mdPadding .controller=${card}>
<runtime-vertical-layout startAligned id="details">
<runtime-text .controller=${descriptor}></runtime-text>
<runtime-input marginTop .controller=${name.input}></runtime-input>
<runtime-input marginTop .controller=${description.input}></runtime-input>
</runtime-vertical-layout>
</runtime-card>
`;
};
Obviously Runtime.dev takes this model to an extreme and is several generations in but I think that the direction of LitElement/HTML 3.0 through a component-controller model is a great evolution.
A few things to note IMO:
The Hybrid Approach of drop-in controllers (Runtime V1 did this), it's too flexible and doesn't scale well. We moved to an entirely controlled structure where if a component supports a controller - it is dependent on that controller. Otherwise it's considered a dumb component and is primarily visual/structural. Additionally all components have one controller that encapsulates a subset of functionality.
Sizing best to be managed externally we have Positioning Engines and Virtualization Engines Dedicated to this - as opposed to being handled internally. Adding a resize observer and then running getBoundingClientRect may drill perf especially if you use it like this often. Also - developed systems being to want to understand how things are sized in relation to each other and how things may render in context of other things rendering around them, this is particularly important for virtualization, masonry layouts, and grids. (Virtualization of Google Photos)
Waiting on children to update/render is also easily implemented if all of your children have controllers as opposed to some of them however - if only some children have controllers and others don't then this introduces inconsistencies/uniformity problems. I would suggest this type of thing to be implemented on the component level as opposed to through a controller infrastructure. The same thing applies to focus management. It works out best if everything that is focusable is also controlled.
I would suggest that elements take in only one controller and that controller is explicit in its capabilities so that the expectations of what is available (Sizing, Theming, Updates, Etc.) becomes standard almost as we expect things such as connectedCallback and attributesChangedCallback within native WC. Treating controllers as directives is something that I would recommend avoiding - the extension based controller system has worked out very well for us: note in the image below our InputController extends an InteractiveController. Which guarantees particular behaviors and functionality as opposed to having to remember if a component has x/y/z controllers such as theming/bounds/etc. This also makes it easier to build systems that manage groups of controllers such as focus engines, placement engines, and state engines.
If you want to get a sense of of this direction and how it impacts WC applications at scale take a look at these:
Component Controller Model & LitElement
Component Controller Model At Scale Google Slides
Nevertheless - a bit of a shotgun thought process here due to controllers simply opening the floodgates on possibilities. Our lit-element WC have been controlled for about a year now and we have learned a lot. I would love to see a community perspective on this as it may impact how we move into the future and perhaps how we can help you avoid the madness we ran into here and there.
Would be happy to have a live chat/meet and go over a couple of these things/answer some questions. Runtime isn't open sourced yet - we are developing our public License right and scoping public demand for such a thing.
It is however actively being Licensed to our partners and we are under serval NDAs with Google, Microsoft, and others so I am available to guide/brainstorm pretty extensively without getting in trouble.
We are currently focused on building an upcoming product but we are always improving Runtime and we would love help bringing it into the community in conjunction with LitElement 3.0
I also read the PR's and Issues for LitElement and LitHTML daily and so I am very informed and happy to give my perspective on design choices being made and why we did or didn't do something similar.
Feel free to reach out personally at tsavo.knott@mesh-hq.com or @KnottTsavo on twitter.
Sorry for the typo's I'm on the run today!
Anyway - have a great day and happy coding!
All the best, -Tsavo
@tvvignesh
@customElement('my-element')
can skip declaration if it is already in the registry instead of failing with an error.We'd rather rely on a standard solution to this like Scoped CustomElementRegistry. Protecting against this exception can mask true errors in a program. We don't think that first-to-register is a reliable way to handle conflicts.
I also wouldn't want to make
@customElement
harder to explain. There's a big benefit to saying that it's just a declarative form ofcustomElements.define()
and behaves the same.Re: VS Code plugins, I get CSS highlighting with lit-plugin: https://marketplace.visualstudio.com/items?itemName=runem.lit-plugin I'm not sure what's happening for you, but I'd use that or file an issue if you already are.
Re: App recommendations. We're definitely looking into addressing these topics directly in docs. I'm not sure we'll call them recommendations or not, but we do want to guide developers towards answers for common needs like routing.
@justinfagnani Thanks for your hint regarding the extension. Switching to the extension you had suggested worked great (able to get syntax highlighting and autocomplete for everything now 😃) I was using https://marketplace.visualstudio.com/items?itemName=bierner.lit-html before which has huge number of downloads.
Regarding scoped custom element registry, I read about the discussion you had initiated here: https://github.com/w3c/webcomponents/issues/716 and your gist here: https://gist.github.com/justinfagnani/d67d5a5175ec220e1f3768ec67a056bc . Looks like it has been 3 years since it started. Would it get implemented? 🤔 And even if it does at some point, I guess it would take ages for all the users to get the updates with all the browser vendors taking their own time to support it 😟 Anyways, I guess I have to go with my hack till then.
Btw, on a side note I remember this talk back when Polymer was the thing: https://www.youtube.com/watch?v=tNulrEbTQf8 Does Youtube still use Polymer or has it migrated to Lit? Any reference architecture we can get from there would be great. Currently I am sticking to the Open-wc recommendations but some real world prod reference would be great to have.
Switching to the extension you had suggested worked great (able to get syntax highlighting and autocomplete for everything now 😃
I miss code collapse support: https://github.com/runem/lit-analyzer/issues/31
I really like LitElement but tooling support is way behind other libraries like React, Vue
Switching to the extension you had suggested worked great (able to get syntax highlighting and autocomplete for everything now 😃
I miss code collapse support: runem/lit-analyzer#31
I really like LitElement but tooling support is way behind other libraries like React, Vue
Yup. I completely agree with you. Lit is great for the most part with all the features it currently has. So, instead of bloating it or changing things up a lot, its more important to put the investment to efforts like what Open-wc (Now modern-web) is doing, emphasising more on the performance and maybe create helpers, tooling and focus more on efforts towards things like this: https://github.com/material-components/material-components-web-components which can be a boon for not just people using Lit but also for people coming from other frameworks.
@tvvignesh
I agree that Lit* (in fact web components) needs improves its ecosystem with e.g. more mature feature rich UI libraries / design systems but in my opinion performance is pretty good as is.
Developer experience should be priority (it's already good but things like missing code collapse in IDE gets in my way from time to time)
@tvvignesh
I agree that Lit* (in fact web components) needs improves its ecosystem with e.g. more mature feature rich UI libraries / design systems but in my opinion performance is pretty good as is.
Developer experience should be priority (it's already good but things like missing code collapse in IDE gets in my way from time to time)
DId you try setting "editor.foldingStrategy": "indentation"
as mentioned there. It worked for me.
DId you try setting "editor.foldingStrategy": "indentation" as mentioned there. It worked for me.
No. See my comment there.
Thats just like i said above: from time to time issues in lit-* tooling requires me to think about it, look for workarounds etc. In a mature ecosystem this does not occurs
@bengfarrell I think this might be nicely addressed with what I've been calling recently "reactive controllers" - helper objects that are able to hook and trigger the host element's lifecycle.
Imagine a controller called
BoundsController
which installs a ResizeObserver and on resize pulls the bounding rect and triggers an update.
Why not go all the way and also trigger life-cycle events from data change observations, like in MVVM? That could replace property change observations and allow for a pluggable way of triggering updates.
I would also like to throw revisiting lit/lit-element#812 in the mix. The issue is about using ES2015 proxies to support reacting to changes to an object property or an element in an array. Currently such assignments are not recognized and need workarounds leading to confusion for some of my colleagues and errors.
Vue 3 is implementing the same feature and as shown by the last comment the implementation is fairly simple. As the next version of lit-html is not going to include IE11 support in the core library I think this is a good time to implement this.
Make custom elements compatible with native elements
@septatrix lit-mobx already does a great job of that: https://github.com/adobe/lit-mobx It's very similar to Vue 3's reactivity library. There's a number of different way that LitElement authors are handling state change tracking, from MobX to Redux, to events + immutable data. I don't think we want to chose a particular way, but enable many of them. Being able to hook update() and call requestUpdate() are powerful primitives for these libraries to use.
@busynest I'm not sure what you mean
@justinfagnani With a proxy based observable approach one can replace a big part of lit-element's property tracking. A nice library to use for that is https://github.com/nx-js/observer-util. I have done exactly that with https://github.com/kwaclaw/KdSoft.lit-mvvm.
I also see this as a superseeding of the current approach. Proxies allow a greater control and are able to react to more changes. Furthermore implementing this natively could be done in only a few lines and not add the weight of a comparatively huge dependency lige mobx, redux etc.
My biggest problem was always that the I couldn't just develop with it because of the module specifiers. In the end the solution I had to use was to use rollup at npm install time to copy to a client/libs directory (most of my apps each has a common node_modules and separate server and client directories). I like that you are considering the out of the box ready to use - but please note that node_modules isn't always located at the root of the client directory.
Other than that lit-html, lit-element and webcomponents loader is all I've needed (other than my own utilities) (I don't build, my production systems don't seem to need it - not even minified anything).
@akc42 I am using Snowpack for the same purpose.
Nice work, excited about "Declarative Events"!
Be able to pass an @query to another child element.
Currently if I need to pass a sibling to another child element I would do something like this:
@query('foo-element') foo!:FooElement;
render() {
return html`
<foo-element></foo-element>
<other-element .foo=${this.foo}></other-element>
`;
}
However that will not work on first render since
Typed PropertyValues
would be really clutch. For static properties getter, I'm sure it could be inferred. not sure about decorators, though.
@property() newPropName: string;
updated(changed: PropertyValues) {
if (changed.has('oldPropName'))
whoops();
}
I would love to see a considered answer to the question, "what does it mean to render without a shadow root?"
In practice, one does not always need to create a shadow root in order to have a useful component implementation. There are a handful of cases today where it would be better not to use a shadow root.
LitElement as of 2.x makes rigid assumptions (as a matter of internal implementation) regarding the usage of a shadow root. This suggests to users that although it may be possible to create components without a shadow root, it is not the intended usage of LitElement. This in turn interrupts the exploration and adoption of novel, higher-order usages of LitElement.
It is not necessary for LitElement to establish a first-class workflow for components that do not use shadow roots. However, the LitElement base class should become a viable candidate for such a workflow, one that does not levy a cognitive or performance tax on authors who seek to use it that way.
Idea for lit-labs
package: consider implementing a portal
directive which could serve for building modals, tooltips etc.
Right now pretty much every more or less complex web components library has its own mechanism. At Vaadin, we use vaadin-overlay
which teleports elements under document body. Other alternatives include scanning DOM tree to manage z-index.
It would be nice to have a recommended approach. There used to be iron-overlay
prototype under PolymerLabs but it hasn't been finished.
https://github.com/Polymer/lit-element/issues/1077#issuecomment-694749771
Does Youtube still use Polymer or has it migrated to Lit?
(totally off topic lol) Yikes. YouTube still uses Polymer?! No wonder its so slow /s. I had been using an extension to use the old YouTube until YouTube broke it earlier this year. :((( um. yay opt-in polyfills. ie needs to die.
Any solution to solve unistore binding to LitElement.
My not ideal solution... I would like to declare dynamically properties for the state props. I must declare observedState object now.
export const connect = store => superclass =>
class extends superclass {
static get properties() {
return {
// The observedState contains selected the store state props by observedStateProps var.
// Any change of observedState will be run render() of LitElement.
observedState: { type: Object },
};
}
constructor() {
super();
this.state = {};
this.oldState = {};
this.observedState = {};
this.observedStateProps = [];
this._stateChanged = this._stateChanged.bind(this);
}
connectedCallback() {
if (super.connectedCallback) super.connectedCallback();
store.subscribe(this._stateChanged);
this._stateChanged(store.getState());
}
disconnectedCallback() {
store.unsubscribe(this._stateChanged);
if (super.disconnectedCallback) super.disconnectedCallback();
}
// The `_stateChanged(state)` method will be called when the state is updated.
_stateChanged(state) {
const observedState = {};
this.observedStateProps.forEach(prop => {
observedState[prop] = state[prop];
});
this.oldState = this.state;
this.state = state;
if (
Object.keys(observedState).length &&
!equals(this.observedState, observedState)
)
this.observedState = observedState;
this.stateChanged(state);
}
// eslint-disable-next-line class-methods-use-this
stateChanged() {}
};
Inspired by
(apologies, as this is somewhat off-topic)
@JosefJezek I have experimented with Unistore-like patterns on top of LitElement. I have a very radioactive / not-well-documented repo with my experiments here: https://github.com/cdata/bag-of-tricks
Usage ends up looking like this:
// A store:
const store = new Store<FooState>({
bar: 'baz'
});
// An action creator that operates on the store:
const setBar = (bar: string): Action<FooState> => (getState, dispatch) => {
return {
...getState(),
bar
};
};
// A connected component:
@customElement('foo-component')
export class FooComponent extends connect<FooState>()(LitElement) {
@property({ type: String })
@selector<FooState>((state) => state.bar)
bar: string?;
#setBar = selfDispatch(setBar);
render() {
return html`
<button @click="${() => this.#setBar('zot')}">Set bar to zot</button>
`;
}
}
// Meanwhile, in some entrypoint:
provide(self, store);
Some details about the above example:
Store
extends EventTarget
, and state changes are announced with dispatchEvent
connect
mixin creates a "connected" component without coupling it to a specific Store
selfDispatch
is a helper that takes any action creator and returns a self-dispatching one (e.g., it automatically invokes store.dispatch(...)
on the currently connected Store
when invoked)provide
helper takes any EventTarget
and sets it up to provide connected components with the designated store
as they are attached down-tree@selector
decorator binds state in the store to properties on the componentThe thing I like about this approach is that it neatly decouples the store from the component, which makes testing a lot simpler. In the course of a test, we can trivially substitute different stores without changing the implementation of a component (or trying to come up with some kind of fancy module substitution for its dependencies).
Anyway, feel free to borrow any of those ideas. I would be happy to talk about it more in an issue thread in that repo, if it interests you.
@justinfagnani Great things are being proposed here, I only wanted to ask you to keep LitElement free from to much "sugar code" and keep LitElement standards, I personally choose LitElement because is near to the platform and it doesn't look like Angular or React or any other Framework/Library...
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
andupdated
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, theexportparts
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. Havingon
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 theupdateComplete
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.