Lifecycle
Lit components use the standard custom element lifecycle methods. In addition Lit introduces a reactive update cycle that renders changes to DOM when reactive properties change.
Standard custom element lifecycle
Permalink to “Standard custom element lifecycle”Lit components are standard custom elements and inherit the custom element lifecycle methods. For information about the custom element lifecycle, see Using the lifecycle callbacks on MDN.
If you need to customize any of the standard custom element lifecycle methods, make sure to call the
super
implementation (such as
super.connectedCallback()
) so the standard Lit functionality is maintained.
constructor()
Permalink to “constructor()”Called when an element is created. Also, it’s invoked when an existing element is upgraded, which happens when the definition for a custom element is loaded after the element is already in the DOM.
Lit behavior
Permalink to “Lit behavior”
Requests an asynchronous update using the
requestUpdate()
method, so when a Lit component gets upgraded, it performs an update immediately.
Saves any properties already set on the element. This ensures values set before upgrade are maintained and correctly override defaults set by the component.
Use cases
Permalink to “Use cases”Perform one time initialization tasks that must be done before the first update . For example, when not using decorators, default values for properties can be set in the constructor, as shown in Declaring properties in a static properties field .
connectedCallback()
Permalink to “connectedCallback()”Invoked when a component is added to the document's DOM.
Lit behavior
Permalink to “Lit behavior”
Lit initiates the first element update cycle after the element is connected. In preparation for rendering, Lit also ensures the
renderRoot
(typically, its
shadowRoot
) is created.
Once an element has connected to the document at least once, component updates will proceed regardless of the connection state of the element.
Use cases
Permalink to “Use cases”
In
connectedCallback()
you should setup tasks that should only occur when the element is connected to the document. The most common of these is adding event listeners to nodes external to the element, like a keydown event handler added to the window. Typically, anything done in
connectedCallback()
should be undone when the element is disconnected — for example, removing event listeners on window to prevent memory leaks.
disconnectedCallback()
Permalink to “disconnectedCallback()”Invoked when a component is removed from the document's DOM.
Lit behavior
Permalink to “Lit behavior”Pauses the reactive update cycle . It is resumed when the element is connected.
Use cases
Permalink to “Use cases”
This callback is the main signal to the element that it may no longer be used; as such,
disconnectedCallback()
should ensure that nothing is holding a reference to the element (such as event listeners added to nodes external to the element), so that it is free to be garbage collected. Because elements may be re-connected after being disconnected, as in the case of an element moving in the DOM or caching, any such references or listeners may need to be re-established via
connectedCallback()
so that the element continues functioning as expected in these scenarios. For example, remove event listeners to nodes external to the element, like a keydown event handler added to the window.
No need to remove internal event listeners. You don't need to remove event listeners added on the component's own DOM—including those added declaratively in your template. Unlike external event listeners, these won't prevent the component from being garbage collected.
attributeChangedCallback()
Permalink to “attributeChangedCallback()”
Invoked when one of the element’s
observedAttributes
changes.
Lit behavior
Permalink to “Lit behavior”
Lit uses this callback to sync changes in attributes to reactive properties. Specifically, when an attribute is set, the corresponding property is set. Lit also automatically sets up the element’s
observedAttributes
array to match the component’s list of reactive properties.
Use cases
Permalink to “Use cases”You rarely need to implement this callback.
adoptedCallback()
Permalink to “adoptedCallback()”Invoked when a component is moved to a new document.
Be aware that
adoptedCallback
is not polyfilled.
Lit behavior
Permalink to “Lit behavior”Lit has no default behavior for this callback.
Use cases
Permalink to “Use cases”This callback should only be used for advanced use cases when the element behavior should change when it changes documents.
Reactive update cycle
Permalink to “Reactive update cycle”In addition to the standard custom element lifecycle, Lit components also implement a reactive update cycle.
The reactive update cycle is triggered when a reactive property changes or when the
requestUpdate()
method is explicitly called. Lit performs updates asynchronously so property changes are batched — if more properties change after an update is requested, but before the update starts, all of the changes are captured in the same update.
Updates happen at microtask timing, which means they occur before the browser paints the next frame to the screen. See Jake Archibald's article on microtasks for more information about browser timing.
At a high level, the reactive update cycle is:
-
An update is scheduled when one or more properties change or when
requestUpdate()
is called. -
The update is performed prior to the next frame being painted.
- Reflecting attributes are set.
- The component’s render method is called to update its internal DOM.
-
The update is completed and the
updateComplete
promise is resolved.
In more detail, it looks like this:
Pre-Update
Update
Post-Update
The changedProperties map
Permalink to “The changedProperties map”
Many reactive update methods receive a
Map
of changed properties. The
Map
keys are the property names and its values are the
previous
property values. You can always find the current property values using
this.
property
or
this[
property
]
.
TypeScript types for changedProperties
Permalink to “TypeScript types for changedProperties”
If you're using TypeScript and you want strong type checking for the
changedProperties
map, you can use
PropertyValues<this>
, which infers the correct type for each property name.
If you're less concerned with strong typing—or you're only checking the property names, not the previous values—you could use a less restrictive type like
Map<string, any>
.
Note that
PropertyValues<this>
doesn't recognize
protected
or
private
properties. If you're checking any
protected
or
private
properties, you'll need to use a less restrictive type.
Changing properties during an update
Permalink to “Changing properties during an update”
Changing a property
during
the update (up to and including the
render()
method) updates the
changedProperties
map, but
doesn't
trigger a new update. Changing a property
after
render()
(for example, in the
updated()
method) triggers a new update cycle, and the changed property is added to a new
changedProperties
map to be used for the next cycle.
Triggering an update
Permalink to “Triggering an update”
An update is triggered when a reactive property changes or the
requestUpdate()
method is called. Since updates are performed asynchronously, any and all changes that occur before the update is performed result in only a
single update
.
hasChanged()
Permalink to “hasChanged()”
Called when a reactive property is set. By default
hasChanged()
does a strict equality check and if it returns
true
, an update is scheduled. See
configuring
hasChanged()
for more information.
requestUpdate()
Permalink to “requestUpdate()”
Call
requestUpdate()
to schedule an explicit update. This can be useful if you need the element to update and render when something not related to a property changes. For example, a timer component might call
requestUpdate()
every second.
The list of properties that have changed is stored in a
changedProperties
map that’s passed to subsequent lifecycle methods. The map keys are the property names and its values are the previous property values.
Optionally, you can pass a property name and a previous value when calling
requestUpdate()
, which will be stored in the
changedProperties
map. This can be useful if you implement a custom getter and setter for a property. See
Reactive properties
for more information about implementing custom getters and setters.
Performing an update
Permalink to “Performing an update”
When an update is performed, the
performUpdate()
method is called. This method calls a number of other lifecycle methods.
Any changes that would normally trigger an update which occur
while
a component is updating do
not schedule a new update
. This is done so that property values can be computed during the update process. Properties changed during the update
are reflected in the
changedProperties
map
, so subsequent lifecycle methods can act on the changes.
shouldUpdate()
Permalink to “shouldUpdate()”Called to determine whether an update cycle is required.
Arguments |
changedProperties
:
Map
with keys that are the names of changed properties and values that are the corresponding previous values.
|
Updates | No. Property changes inside this method do not trigger an element update. |
Call super? | Not necessary. |
Called on server? | No. |
If
shouldUpdate()
returns
true
, which it does by default, then the update proceeds normally. If it returns
false
, the rest of the update cycle will not be called but the
updateComplete
Promise is still resolved.
You can implement
shouldUpdate()
to specify which property changes should cause updates. Use the map of
changedProperties
to compare current and previous values.
willUpdate()
Permalink to “willUpdate()”
Called before
update()
to compute values needed during the update.
Arguments |
changedProperties
:
Map
with keys that are the names of changed properties and values that are the corresponding previous values.
|
Updates? | No. Property changes inside this method do not trigger an element update. |
Call super? | Not necessary. |
Called on server? | Yes. |
Implement
willUpdate()
to compute property values that depend on other properties and are used in the rest of the update process.
update()
Permalink to “update()”Called to update the component's DOM.
Arguments |
changedProperties
:
Map
with keys that are the names of changed properties and values that are the corresponding previous values.
|
Updates? | No. Property changes inside this method do not trigger an element update. |
Call super? | Yes. Without a super call, the element’s attributes and template will not update. |
Called on server? | No. |
Reflects property values to attributes and calls
render()
to update the component’s internal DOM.
Generally, you should not need to implement this method.
render()
Permalink to “render()”
Called by
update()
and should be implemented to return a renderable result (such as a
TemplateResult
) used to render the component's DOM.
Arguments | None. |
Updates? | No. Property changes inside this method do not trigger an element update. |
Call super? | Not necessary. |
Called on server? | Yes. |
The
render()
method has no arguments, but typically it references component properties. See
Rendering
for more information.
Completing an update
Permalink to “Completing an update”
After
update()
is called to render changes to the component's DOM, you can perform actions on the component's DOM using these methods.
firstUpdated()
Permalink to “firstUpdated()”
Called after the component's DOM has been updated the first time, immediately before
updated()
is called.
Arguments |
changedProperties
:
Map
with keys that are the names of changed properties and values that are the corresponding previous values.
|
Updates? | Yes. Property changes inside this method schedule a new update cycle. |
Call super? | Not necessary. |
Called on server? | No. |
Implement
firstUpdated()
to perform one-time work after the component's DOM has been created. Some examples might include focusing a particular rendered element or adding a
ResizeObserver
or
IntersectionObserver
to an element.
updated()
Permalink to “updated()”Called whenever the component’s update finishes and the element's DOM has been updated and rendered.
Arguments |
changedProperties
:
Map
with keys that are the names of changed properties and values that are the corresponding previous values.
|
Updates? | Yes. Property changes inside this method trigger an element update. |
Call super? | Not necessary. |
Called on server? | No. |
Implement
updated()
to perform tasks that use element DOM after an update. For example, code that performs animation may need to measure the element DOM.
updateComplete
Permalink to “updateComplete”
The
updateComplete
promise resolves when the element has finished updating. Use
updateComplete
to wait for an update. The resolved value is a boolean indicating if the element has finished updating. It will be
true
if there are no pending updates after the update cycle has finished.
When an element updates, it may cause its children to update as well. By default, the
updateComplete
promise resolves when the element's update has completed, but does not wait for any children to have completed their updates. This behavior may be customized by overriding
getUpdateComplete
.
There are several use cases for needing to know when an element's update has completed:
-
Tests When writing tests you can await the
updateComplete
promise before making assertions about a component’s DOM. If the assertions depend on updates completing for the component's entire descendant tree, awaitingrequestAnimationFrame
is often a better choice, since Lit's default scheduling uses the browser's microtask queue, which is emptied prior to animation frames. This ensures all pending Lit updates on the page have completed before therequestAnimationFrame
callback. -
Measurement Some components may need to measure DOM in order to implement certain layouts. While it is always better to implement layouts using pure CSS rather than JavaScript-based measurement, sometimes CSS limitations make this unavoidable. In very simple cases, and if you're measuring Lit or ReactiveElement components, it may be sufficient to await
updateComplete
after state changes and before measuring. However, becauseupdateComplete
does not await the update of all descendants, we recommend usingResizeObserver
as a more robust way to trigger measurement code when layouts change. -
Events It is a good practice to dispatch events from components after rendering has completed, so that the event's listeners see the fully rendered state of the component. To do so, you can await the
updateComplete
promise before firing the event.
The
updateComplete
promise rejects if there's an unhandled error during the update cycle. For more information, see
Handling errors in the update cycle
.
Handling errors in the update cycle
Permalink to “Handling errors in the update cycle”
If you have an uncaught exception in a lifecycle method like
render()
or
update()
, it causes the
updateComplete
promise to reject. If you have code in a lifecycle method that can throw an exception, it's good practice to put it inside a
try
/
catch
statement.
You may also want to use a
try
/
catch
if you're awaiting the
updateComplete
promise:
In some cases, code may throw in unexpected places. As a fallback, you can add a handler for
window.onunhandledrejection
to catch these issues. For example, you could use this report errors back to a backend service to help diagnose issues that are hard to reproduce.
Implementing additional customization
Permalink to “Implementing additional customization”This section covers some less-common methods for customizing the update cycle.
scheduleUpdate()
Permalink to “scheduleUpdate()”
Override
scheduleUpdate()
to customize the timing of the update.
scheduleUpdate()
is called when an update is about to be performed, and by default it calls
performUpdate()
immediately. Override it to defer the update—this technique can be used to unblock the main rendering/event thread.
For example, the following code schedules the update to occur after the next frame paints, which can reduce jank if the update is expensive:
If you override
scheduleUpdate()
, it's your responsibility to call
super.scheduleUpdate()
to perform the pending update.
Async function optional.