Calling querySelector Before the Component Renders
“The DOM doesn't exist in connectedCallback. Stop looking for it there.”
What Happened
I needed to focus an input field when a modal opened in my Delivery Hub project. Put this.template.querySelector('lightning-input').focus() in connectedCallback. Null reference error every time. Spent 30 minutes adding console.logs everywhere before remembering that connectedCallback fires before the template renders. The DOM elements literally don't exist yet.
The Wrong Way
// myModal.js
import { LightningElement } from 'lwc';
export default class MyModal extends LightningElement {
connectedCallback() {
// DOM doesn't exist yet!
const input = this.template.querySelector('lightning-input');
input.focus(); // TypeError: Cannot read property 'focus' of null
}
}The Right Way
// myModal.js
import { LightningElement } from 'lwc';
export default class MyModal extends LightningElement {
renderedCallback() {
// DOM exists now, but this fires on every re-render
if (this._hasRendered) return;
this._hasRendered = true;
const input = this.template.querySelector('lightning-input');
if (input) {
// eslint-disable-next-line @lwc/lwc/no-async-operation
setTimeout(() => input.focus(), 100);
}
}
_hasRendered = false;
}The Lesson
connectedCallback = component connected, no DOM yet. renderedCallback = DOM exists. Use a flag to run logic only on first render.
Enjoyed this? Get more like it.
Glen's Musings — AI, investing, and building things. Occasional. Free.
More LWC Mistakes
Mixing Wire and Imperative Apex Calls for the Same Data
Pick one: reactive wire or imperative call. Mixing them creates ghost data.
Read morePainfulMutating Objects Without Triggering Reactivity
LWC tracks property assignment, not deep object mutations.
Read moreAnnoyingCustom Events Not Reaching Parent Components
CustomEvent doesn't bubble or compose by default. Your parent never heard you.
Read more