Skip to content

The updated() Method

updated() runs immediately after every render, once the new template has been applied to the DOM. This lesson covers its signature and the patterns it's typically used for.

What updated() Is For

Unlike render(), which must stay a pure function returning a template, updated() runs *after* the DOM has already been updated, which makes it the right place for side effects that need to inspect or interact with the real, current DOM — moving focus, measuring an element's size, or triggering a CSS transition.

updated() receives a Map<PropertyKey, unknown> called changedProperties, mapping each property that changed in this update to its *previous* value, which lets you write precise, targeted reactions instead of generic logic that reruns on every unrelated change.

class AutoFocusInput extends LitElement {
  @property({ type: Boolean }) active = false;

  updated(changedProperties) {
    if (changedProperties.has('active') && this.active) {
      this.shadowRoot.querySelector('input')?.focus();
    }
  }

  render() {
    return html`<input ?disabled=${!this.active} />`;
  }
}

Focus is moved only when active specifically changed to true — not on every unrelated re-render.

updated() Signature

updated(changedProperties: Map<PropertyKey, unknown>) {
  super.updated(changedProperties); // optional, but good practice
  if (changedProperties.has('somePropName')) {
    // react to that specific change
  }
}
  • changedProperties.get('prop') returns the *previous* value of prop, useful for comparing old vs. new.
  • this.prop inside updated() already reflects the *new*, current value.
  • updated() runs after every single render, including the very first one — for first-render-only logic, use firstUpdated() instead.
  • Calling super.updated(changedProperties) isn't strictly required (the base implementation is a no-op) but is good defensive practice.

updated() Use Cases

Common side effects that belong in updated(), not render().

Task Why updated()
Move focus to a newly shown element Requires the DOM to already reflect the new state
Measure an element's size (getBoundingClientRect) Layout is only accurate after the DOM update
Trigger a CSS transition by toggling a class late Needs the 'before' state committed to the DOM first
Sync a non-Lit third-party widget with new props Imperative libraries need explicit update calls
Scroll a newly selected item into view Depends on the updated DOM layout

Comparing Old and New Values

Because changedProperties maps each changed property to its *previous* value, you can compare exactly what changed without storing your own extra 'previous value' field elsewhere on the component.

updated(changedProperties) {
  if (changedProperties.has('page')) {
    const previousPage = changedProperties.get('page');
    if (this.page > previousPage) {
      this.#animateForward();
    } else {
      this.#animateBackward();
    }
  }
}

Syncing an Imperative Third-Party Library

Many charting or mapping libraries expose an imperative update(options) method rather than being declarative. updated() is the natural place to call that method whenever the relevant Lit properties change, bridging Lit's declarative model with an imperative dependency.

updated(changedProperties) {
  if (changedProperties.has('chartData') && this.#chartInstance) {
    this.#chartInstance.setData(this.chartData);
  }
}

The chart's own DOM node is typically created once in firstUpdated() and then just updated here on subsequent changes.

Common Mistakes

  • Setting a reactive property inside updated() without a guard condition, risking an infinite update loop.
  • Performing DOM measurements inside render() instead of updated(), before the new DOM has actually been committed.
  • Ignoring changedProperties entirely and re-running expensive logic on every single update regardless of cause.
  • Forgetting firstUpdated() exists for genuinely one-time-only DOM setup, duplicating that setup inside updated() with manual guards instead.

Key Takeaways

  • updated() runs after every render, once the DOM already reflects the new state.
  • The changedProperties map lets you react precisely to which properties changed and see their previous values.
  • It's the right place for focus management, DOM measurement, and syncing imperative third-party libraries.
  • Setting a reactive property inside updated() needs a guard condition to avoid an infinite update loop.

Pro Tip

If you find yourself setting a reactive property inside updated(), pause and check whether the value could instead be computed directly inside render() — genuinely needing to set state inside updated() is less common than it might first seem.