Defer Attribute
Definition
Written as script src="app.js" defer, it changes when a script runs rather than whether it runs. Without it the parser halts at the tag, fetches the file and executes it before reading another line of HTML. With it the download happens alongside parsing and execution waits until parsing has finished, immediately before the DOMContentLoaded event fires. Several deferred scripts keep document order, so a library tagged defer still runs before the file that depends on it. The attribute applies only to external files: defer on an inline script is ignored, as it is on a module, which already behaves this way by default.
Why It Matters
Two problems disappear at once. The script stops blocking the first paint, which on a phone is worth a few hundred milliseconds per file. And it stops running before the elements it needs exist, which is the cause of most Cannot read properties of null errors on a page somebody else wrote — the familiar workaround of moving every script tag to the bottom of the body is the same idea done by hand. Order survives too, so unlike async you can defer a whole chain of dependent files with nothing running early. The one thing to watch is that any script relying on document.write cannot be deferred at all.
How It Works
The parser meets the tag, starts the download on a separate connection and carries straight on building the DOM. Files that finish early are queued rather than run. Once parsing is complete the queue executes in document order, then DOMContentLoaded fires, then images finish and the load event follows. Because execution happens after the document is parsed, every element is present and the script can query the page directly without wrapping itself in a DOMContentLoaded listener. In practice defer is the right default for anything that touches the page, and async is right only for scripts nothing else depends on.
Real-World Example
An AI-generated dashboard uploaded to 99helpers and served at team-board.99helpers.site carries three script tags in the head, shows a blank panel on load and logs two null errors. Adding defer to all three, in the order library then helpers then app, clears both symptoms: the first paint arrives about 400 milliseconds sooner and the elements exist by the time the code goes looking for them. Nothing inside the JavaScript itself changed.
Common Mistakes
- ✕Adding defer to an inline script — the attribute is ignored and the code still runs the moment the parser reaches it
- ✕Mixing defer and async across files that depend on each other — the async one can land first and call a library that has not run yet
- ✕Deferring a file that an inline script calls straight away — the inline code runs during parsing, when the deferred file has not executed
Related Terms
Async Attribute
The async attribute lets a script download in the background and run the moment it is ready, without waiting for the rest of the HTML. It does not block parsing, and it does not keep order either.
Render-Blocking Resource
A render-blocking resource is a file the browser has to download and process before it can show anything at all. Stylesheets and ordinary script tags in the head block; almost nothing else does.
Console Error
A console error is a message the browser writes when something on the page fails — a script throwing, a file that would not load, a request the browser refused. It is the first place to look when a page misbehaves.
Inline JavaScript
JavaScript written inside the HTML document — in a script element with no src attribute, or in an attribute such as onclick — rather than loaded from a .js file. It keeps a generated page to one file.
ES Module
JavaScript's built-in module format, using import and export statements. Browsers load ES modules natively from a script tag with type set to module, with no bundler involved.
Put a file online in seconds
Drop in a document, an image, a page or a whole static website and share the link — free, with no build step and no server to set up.
Host a file free →