You can use mixins to customize a Lit component by adding API or overriding its lifecycle callbacks.
Mixin basicsPermalink to “Mixin basics”
For ease of reading, the samples on this page elide some of the TypeScript types for mixin functions. See Mixins in TypeScript for details on proper typing of mixins in TypeScript.
To define a mixin, write a function that takes a
superClass, and returns a new class that extends it, adding fields and methods as needed:
To apply a mixin, simply pass a class to generate a subclass with the mixin applied. Most commonly, users will apply the mixin directly to a base class when defining a new class:
Mixins can also be used to create concrete subclasses that users can then extend like a normal class, where the mixin is an implementation detail:
- Class mixins on MDN
- Mixins in the TypeScript handbook.
- Dedupe mixin library by open-wc, including a discussion of when mixin usage may lead to duplication, and how to use a deduping library to avoid it.
- Mixin conventions followed by Elix web component library. While not Lit-specific, contains thoughtful suggestions around applying conventions when defining mixins for web components.
Creating mixins for LitElementPermalink to “Creating mixins for LitElement”
Mixins applied to LitElement can implement or override any of the standard custom element lifecycle callbacks like the
connectedCallback(), as well as any of the reactive update lifecycle callbacks like
For example, the following mixin would log when the element is created, connected, and updated:
Note that a mixin should always make a super call to the standard custom element lifecycle methods implemented by
LitElement. When overriding a reactive update lifecycle callback, it is good practice to call the super method if it already exists on the superclass (as shown above with the optional-chaining call to
Also note that mixins can choose to do work either before or after the base implementation of the standard lifecycle callbacks via its choice of when to make the super call.
Mixins can also add reactive properties, styles, and API to the subclassed element.
The mixin in the example below adds a
highlight reactive property to the element and a
renderHighlight() method that the user can call to wrap some content. The wrapped content is styled yellow when the
highlight property/attribute is set.
Note in the example above, the user of the mixin is expected to call the
renderHighlight() method from their
render() method, as well as take care to add the
static styles defined by the mixin to the subclass styles. The nature of this contract between mixin and user is up to the mixin definition and should be documented by the mixin author.
Mixins in TypeScriptPermalink to “Mixins in TypeScript”
LitElement mixins in TypeScript, there are a few details to be aware of.
Typing the superclassPermalink to “Typing the superclass”
You should constrain the
superClass argument to the type of class you expect users to extend, if any. This can be accomplished using a generic
Constructor helper type as shown below:
The above example ensures that the class being passed to the mixin extends from
LitElement, so that your mixin can rely on callbacks and other API provided by Lit.
Typing the subclassPermalink to “Typing the subclass”
Although TypeScript has basic support for inferring the return type for the subclass generated using the mixin pattern, it has a severe limitation in that the inferred class must not contain members with
protected access modifiers.
LitElement itself does have private and protected members, by default TypeScript will error with "Property '...' of exported class expression may not be private or protected." when returning a class that extends
There are two workarounds that both involve casting the return type from the mixin function to avoid the error above.
When a mixin does not add new public/protected APIPermalink to “When a mixin does not add new public/protected API”
If your mixin only overrides
LitElement methods or properties and does not add any new API of its own, you can simply cast the generated class to the super class type
T that was passed in:
When a mixin adds new public/protected APIPermalink to “When a mixin adds new public/protected API”
If your mixin does add new protected or public API that you need users to be able to use on their class, you need to define the interface for the mixin separately from the implementation, and cast the return type as the intersection of your mixin interface and the super class type:
Applying decorators in mixinsPermalink to “Applying decorators in mixins”
Due to limitations of TypeScript's type system, decorators (such as
@property()) must be applied to a class declaration statement and not a class expression.
In practice this means mixins in TypeScript need to declare a class and then return it, rather than return a class expression directly from the arrow function.