firstUpdated() runs exactly once, right after the component's very first render has been committed to the DOM. This lesson covers what makes it different from updated() and when to use it.
firstUpdated() Runs Exactly Once
firstUpdated() is called immediately after the first updated() call, but never again for the lifetime of that component instance — even across multiple connectedCallback/disconnectedCallback cycles if the element is moved around the DOM. This makes it the right place for setup that should genuinely happen only once, after the DOM first exists.
A common use case is initializing a third-party, non-Lit widget (a chart library, a rich text editor) that needs a real DOM node to attach to — something that isn't available yet during connectedCallback, but is guaranteed to exist by the time firstUpdated() runs.
The chart instance is created exactly once in firstUpdated(), then kept up to date on subsequent renders inside updated().
firstUpdated() Signature
firstUpdated(changedProperties: Map<PropertyKey, unknown>) {
super.firstUpdated(changedProperties); // optional, no-op by default
// one-time DOM setup here
}
Receives the same changedProperties map as updated(), though it's less commonly needed here.
Guaranteed to run only once per component instance, unlike updated() which runs on every render.
This is the earliest point at which this.shadowRoot reliably contains your rendered template's actual DOM nodes.
For layout measurements that depend on CSS being fully applied, you may still need to wait an additional frame (requestAnimationFrame) in rare cases.
firstUpdated() vs updated() vs connectedCallback()
Choosing the right hook for one-time setup.
Hook
Runs
Has Rendered DOM?
connectedCallback()
Every connection
No — before first render
firstUpdated()
Exactly once, ever
Yes — first render just completed
updated()
Every render
Yes — this render just completed
Measuring Rendered Elements
Reading layout information like getBoundingClientRect() requires the element to actually be rendered and laid out. firstUpdated() is a natural place to take an initial measurement, though if the size can change later, you'll still want a ResizeObserver set up alongside it for ongoing updates.
You could simulate firstUpdated() with your own boolean flag checked inside updated(), but using the dedicated hook is clearer, avoids repeating that guard logic across components, and matches what other Lit developers reading your code will already expect.
Re-implementing 'run once' logic manually inside updated() instead of using the dedicated firstUpdated() hook.
Assuming firstUpdated() runs again after the element is disconnected and reconnected — it does not, it's tied to the instance's first render only.
Reading layout measurements that depend on external stylesheets or fonts before those resources have actually finished loading.
Forgetting to set up a ResizeObserver (or similar) for elements whose size can change after the initial measurement in firstUpdated().
Key Takeaways
firstUpdated() runs exactly once per component instance, right after the first render commits to the DOM.
It's the ideal place to initialize non-Lit, imperative third-party widgets that need a real DOM node.
It's also useful for one-time layout measurements, often paired with a ResizeObserver for ongoing changes.
Prefer this dedicated hook over manually simulating 'run once' logic inside updated().
Pro Tip
When wrapping a third-party imperative widget, split the work cleanly: create the instance once in firstUpdated(), then push all subsequent prop changes into it via updated() — this mirrors exactly how Lit's own reactive model expects one-time setup versus ongoing updates to be separated.
You now understand one-time setup with firstUpdated(). Next, learn about disconnectedCallback and proper cleanup.