Lit 3 upgrade guide

If you are looking to migrate from Lit 1.x to Lit 2.x, see the Lit 2 upgrade guide.

Lit 3.0 has very few breaking changes from Lit 2.x:

  • IE11 is no longer supported.
  • Lit's npm modules are now published as ES2021.
  • APIs marked deprecated in Lit 2.x releases have been removed.
  • SSR hydration support modules have moved to the @lit-labs/ssr-client package.
  • Type only: Type of ReactiveElement's renderRoot and createRenderRoot() have been updated.
  • Support was removed for Babel decorators version "2018-09".
  • Decorator behavior has been unified between TypeScript experimental decorators and standard decorators.
    • As a consequence, if you use TypeScript you'll need to upgrade to at least TypeScript v5.2, to get the updated types for both kinds of decorators.

For the vast majority of users there should be no required code changes to upgrade from Lit 2 to Lit 3. Most apps and libraries should be able to extend their npm version ranges to include both 2.x and 3.x, like "^2.7.0 || ^3.0.0".

Lit 2.x and 3.0 are interoperable – templates, base classes, and directives from one version of Lit will work with those from another.

Lit 2 was published as ES2019, and Lit 3 is now published as ES2021 which has wide support in modern browsers and build tools. This may be a breaking change if you need to support older browser versions and your current tooling can't parse ES2021.

Webpack 4's internal parser doesn't support nullish coalescing (??), logical assignment (??=), or optional chaining (?.), which are syntaxes introduced in ES2021 so will throw a Module parse failed: Unexpected token when encountering those.

The preferred solution is to upgrade to Webpack 5 which does support parsing these newer JS syntax. However if you are unable to do so, it is possible to use babel-loader to transform Lit 3 code to work with Webpack 4.

To transpile Lit 3 in Webpack 4, install the following required babel packages:

And add a new rule that is similar to the following (you may need to modify it based on your specific project):

JavaScript decorators have recently been standardized by TC39 and are at Stage 3 of the four-stage standardization process. Stage 3 is when JavaScript implementations such as VMs and compilers start implementing the stable specification. TypeScript 5.2 and Babel 7.23 have implemented the standard recently.

This means that there is more than one version of the decorator API in existence: standard decorators, TypeScript's experimental decorators, and previous proposals that Babel has implemented, such as version "2018-09".

While Lit 2 supported TypeScript experimental decorators and Babel's "2018-09" decorators, Lit 3 now supports standard decorators and TypeScript experimental decorators.

The Lit 3 decorators are mostly backwards compatible with the Lit 2 TypeScript decorators - most likely no changes are needed.

Some minor breaking changes were necessary to make the Lit decorators behave consistently between both experimental and standard decorator modes.

Changes to Lit decorator behavior in Lit 3.0:

If your Lit 2.x project does not have deprecation warnings you should not be impacted by this list.

Removed UpdatingElement alias for ReactiveElement

Permalink to “Removed UpdatingElement alias for ReactiveElement”

Replace Lit 2.x usage of UpdatingElement with ReactiveElement. This is not a functional change as UpdatingElement was aliasing ReactiveElement.

Removed re-export of decorators from lit-element

Permalink to “Removed re-export of decorators from lit-element”

Lit 3.0 built-in decorators are no longer exported by lit-element, and should instead be imported from lit/decorators.js.

Deprecated queryAssignedNodes(slot: string, flatten: bool, selector: string) decorator signature removed

Permalink to “Deprecated queryAssignedNodes(slot: string, flatten: bool, selector: string) decorator signature removed”

Migrate any usage of queryAssignedNodes taking a selector to use queryAssignedElements.

Usages without a selector now must take an options object.

Server side rendering experimental hydration modules removed from lit, lit-element, and lit-html

Permalink to “Server side rendering experimental hydration modules removed from lit, lit-element, and lit-html”

Experimental hydration support has been moved out of core libraries and into @lit-labs/ssr-client.

[Type only]: Type of renderRoot and createRenderRoot() have been updated

Permalink to “[Type only]: Type of renderRoot and createRenderRoot() have been updated”

This is a type only change with no runtime effect.

The type of ReactiveElement.renderRoot was changed from Element | ShadowRoot to HTMLElement | DocumentFragment and the return type of ReactiveElement.createRenderRoot() was changed from HTMLElement | ShadowRoot to HTMLElement | DocumentFragment. This makes them congruent with each other, as well as lit-html's render().

This change generally should not affect code that simply accesses this.renderRoot. However, any code that had explicit type annotations for their former types should be updated.

While Lit 3 adds support for standard decorators, we still recommend that TypeScript users stay with experimental decorators. This is because the emitted code for standard decorators from the TypeScript and Babel compilers is quite large at the moment.

We will recommend standard decorators for production when browsers support them, or when we release decorator transform support in our new Lit compiler.

But you can try standard decorators now, and they work in TypeScript 5.2 and above and Babel 7.23 with the @babel/plugin-proposal-decorators plugin.

Install TypeScript 5.2 or later, and remove the "experimentalDecorators" setting from your tsconfig if it's present.

Install Babel 7.23 or later, and @babel/plugin-proposal-decorators. Be sure to pass the "version": "2023-05" option to the plugin.

Add the accessor keyword to decorated fields

Permalink to “Add the accessor keyword to decorated fields”

Standard decorators are not allowed to change the kind of class member they decorate. Decorators that need to create getters and setters must be applied to existing getters and setters. To make this more ergonomic, the decorators standard adds the accessor keyword which creates "auto accessors" when applied to a class field. Auto accessors look and act much like class fields, but create accessors on the prototype backed by private storage.

The @property(), @state(), @query(), @queryAll(), @queryAssignedElements() and @queryAssignedNode() decorators require the accessor keyword.

Example:

Standard decorators can only replace the class member they're directly applied to. Lit decorators need to intercept property setting, so the decorators must be applied to setters. This is different from the Lit 2 recommendation of applying decorators to getters.

For @property() and @state() you can also remove any this.requestUpdate() calls in the setter since this is done automatically now. If you need to not have requestUpdate() called, you'll have to use the noAccessor property option.

Note that for @property() and @state() the getter will be called by the decorator when setting the property to retrieve the old value. This means that you must define both a getter and a setter.

Before:

After: