Question
When adding JavaScript to an HTML document, where should the <script> tags be placed? I remember hearing that placing scripts in the <head> can be problematic, but putting them at the start of the <body> can also delay rendering because the browser must parse the JavaScript before continuing. That seems to make the end of the <body> a sensible option.
So, what is the correct place to put <script> tags?
I am specifically using jQuery, but a general HTML and JavaScript explanation is also helpful.
Short Answer
By the end of this page, you will understand why script placement affects page loading, rendering, and DOM access. You will learn when to put scripts in the <head>, when to place them near the end of <body>, and how defer and async change the usual rules.
Concept
JavaScript in HTML is not just about where the code lives. It affects when the browser stops to download it, parses it, and runs it.
Why script placement matters
When the browser reads HTML, it processes the document from top to bottom. A normal <script> tag without special attributes is typically render-blocking:
- the browser pauses HTML parsing
- it may fetch the script file
- it executes the script
- only then does it continue parsing the page
That means script placement can affect:
- page rendering speed
- whether elements already exist in the DOM when your code runs
- user experience, especially on slower networks
The traditional advice
Historically, developers often placed scripts just before </body> because:
- the HTML content loads first
- the page becomes visible sooner
- scripts run after most of the DOM has already been parsed
Example:
<body>
<h1>Hello</h1>
<p>Content appears before script runs.</p>
<script src="app.js"></script>
</body>
This is still a valid pattern.
The modern best practice
Today, a very common approach is to place scripts in the <head> and use defer:
<head>
<script src="app.js" defer></script>
</head>
defer tells the browser:
- download the script while parsing HTML
- do not run it immediately
- execute it after the HTML document has been parsed
This gives you the benefits of:
- organized markup in the
<head> - non-blocking page parsing
- safe access to DOM elements after parsing is complete
General rule
- Use
<script defer>in<head>for most page scripts. - Use end of
<body>if you are not usingdeferand want scripts to run after the page markup. - Use
asynconly for scripts that do not depend on DOM order or other scripts, such as analytics.
About jQuery
If you use jQuery and your code depends on the page elements being present, you need to ensure either:
- jQuery and your script load after the HTML elements exist, or
- your code waits for the DOM to be ready
For example:
<head>
<script src="jquery.js" defer></script>
<script src="app.js" defer></script>
</head>
Or with older patterns:
$(document).ready(function () {
// safe to use DOM here
});
The main concept is: the right place depends on when the script must run and whether it blocks parsing.
Mental Model
Think of the browser like someone reading instructions while building furniture.
- The HTML is the assembly guide.
- The browser reads it from top to bottom.
- A normal
<script>tag is like an urgent note saying: stop building and do this task right now.
If that note appears too early, the builder stops before the furniture is fully assembled.
Using defer is like saying: download these extra instructions now, but wait until the main assembly is done before following them.
Putting a script at the end of <body> is like placing the extra instructions after the main guide, so the builder sees them only after most of the work is already complete.
So the key idea is:
- normal scripts interrupt parsing
- deferred scripts wait until parsing finishes
- async scripts happen independently and may run at unpredictable times
Syntax and Examples
Core syntax
1. Traditional script at the end of <body>
<body>
<h1 id="title">Welcome</h1>
<script src="app.js"></script>
</body>
Use this when you want the DOM mostly available before the script runs.
2. Modern script in <head> with defer
<head>
<script src="app.js" defer></script>
</head>
This is a common best practice for application code.
3. Async script
<head>
<script src="analytics.js" async></script>
</head>
Use this for independent scripts like analytics, ads, or tracking.
Beginner-friendly example
HTML
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Script Placement Example</title>
<script src="app.js" defer></script>
</head>
<body>
<h1 id="message">Loading...</h1>
</body>
</html>
JavaScript
const heading = document.getElementById("message");
heading.textContent = "Page is ready";
Why this works
Because defer waits until the HTML has been parsed, the <h1> element already exists when the script runs.
jQuery example
<!doctype html>
<html>
<head>
<script src="jquery.js" defer></script>
<script src="app.js" defer></script>
</head>
<body>
<button id="saveBtn">Save</button>
</body>
</html>
$(function () {
$("#saveBtn").on("click", function () {
alert("Saved");
});
});
This works because:
- jQuery loads before
app.js - both scripts are deferred
- the DOM is parsed before execution
Step by Step Execution
Consider this HTML:
<!doctype html>
<html>
<head>
<script src="app.js" defer></script>
</head>
<body>
<p id="status">Waiting...</p>
</body>
</html>
And this JavaScript:
const statusEl = document.getElementById("status");
statusEl.textContent = "Ready";
What happens step by step
- The browser starts reading the HTML document.
- It reaches
<script src="app.js" defer></script>in the<head>. - Because of
defer, the browser starts downloadingapp.jsbut does not stop parsing the HTML. - The browser continues into the
<body>. - It creates the
<p id="status">element in the DOM. - HTML parsing finishes.
- The browser executes
app.js. document.getElementById("status")finds the paragraph element.- The text changes from
Waiting...toReady.
Compare with a blocking script
If you wrote this instead:
<head>
<script src="app.js"></script>
</head>
<body>
<p id="status">Waiting...</p>
</body>
Then app.js would run before the browser has parsed the <p> element. In that case:
const statusEl = document.getElementById("status");
would likely return null, because the element does not exist yet.
Real World Use Cases
Common practical uses
Application scripts
Main frontend code for a website or web app is often loaded with defer:
<script src="main.js" defer></script>
This lets the page parse first while still organizing scripts in the <head>.
UI interactions
Scripts that attach click handlers, form validation, or menu behavior usually need the DOM to exist first.
Examples:
- opening a mobile menu
- validating a signup form
- attaching event listeners to buttons
- updating text on the page
Analytics and tracking
Independent scripts are often loaded with async because execution order usually does not matter.
Examples:
- analytics
- ad scripts
- tracking pixels
- third-party widgets
Server-rendered pages
Many server-rendered applications output HTML first and then attach JavaScript after the markup is present. This can be done with:
- scripts at the end of
<body> - deferred scripts in
<head>
jQuery-based pages
Older codebases commonly:
- include jQuery first
- include plugin files next
- include custom code last
- place everything near the end of
<body>or use DOM-ready wrappers
Example:
<script src="jquery.js"></script>
<script src="plugin.js"></script>
<script src="app.js"></script>
Real Codebase Usage
In real projects, developers usually choose script placement based on dependency order, rendering performance, and how soon code must run.
Common patterns
1. Deferred application bundles
A typical modern setup:
<head>
<script src="vendor.js" defer></script>
<script src="app.js" defer></script>
</head>
Why this is common:
- files download while HTML is parsed
- execution order is preserved for deferred scripts
- DOM is ready when the scripts run
2. Guarding against missing elements
Even when scripts load correctly, developers often protect code:
const modal = document.getElementById("modal");
if (!modal) {
return;
}
This is useful when the same script runs on multiple pages.
3. Early returns
A common pattern in real code:
const button = document.getElementById("saveBtn");
if (!button) return;
button.addEventListener("click", () => {
console.log("Saved");
});
4. jQuery DOM-ready wrappers in older projects
$(function () {
$(".menu-toggle").on("click", function () {
$(".menu").toggleClass("open");
});
});
This remains common in legacy codebases.
5. Isolating third-party scripts
Developers often keep analytics or ads separate from app logic because they may load with async and should not affect application behavior.
Practical guidance
- Put dependent scripts in a predictable order.
- Use
deferfor most application logic. - Use
asynconly when order does not matter. - Avoid inline event handlers like
onclickwhen possible. - Attach behavior from JavaScript files instead of mixing logic into HTML.
Common Mistakes
1. Running code before the DOM exists
Broken example:
<head>
<script>
const el = document.getElementById("title");
el.textContent = "Hello";
</script>
</head>
<body>
<h1 id="title">Original</h1>
</body>
Problem:
- the script runs before the
<h1>is parsed elisnull
Fix:
- move the script to the end of
<body>, or - use
defer
2. Using async for dependent scripts
Broken example:
<script src="jquery.js" async></script>
<script src="app.js" async></script>
Problem:
app.jsmay run before jQuery has loaded- execution order is not guaranteed
Fix:
<script src="jquery.js" defer></script>
<script src="app.js" defer></script>
3. Forgetting script order
Broken example:
<script src="app.js" defer></script>
<script src="jquery.js" defer></script>
Problem:
app.jsmay expect jQuery before it exists
Fix:
<script src="jquery.js" defer></script>
<script src="app.js" defer></script>
4. Mixing behavior into HTML
Less ideal example:
<a href="#" onclick="saveData()">Save</a>
Why to avoid it:
- mixes structure and behavior
- harder to maintain
- harder to reuse and test
Better:
<a href="#" id="saveLink">Save</a>
document.getElementById("saveLink").addEventListener("click", function (event) {
event.preventDefault();
saveData();
});
5. Assuming every script belongs in the same place
Not all scripts are equal:
- app code often uses
defer - analytics often use
async - very small inline config may appear before another script if needed
Choose placement based on purpose, not habit.
Comparisons
| Approach | Where placed | Blocks HTML parsing? | Keeps execution order? | Good for |
|---|---|---|---|---|
Normal <script> | <head> or <body> | Yes | Yes | Small scripts that must run immediately |
<script> at end of <body> | End of body | Less harmful to rendering | Yes | Traditional DOM-dependent scripts |
<script defer> | Usually <head> | No during parsing | Yes | Most application code |
<script async> | Usually <head> | No during parsing | No | Analytics, ads, independent third-party scripts |
defer vs async
| Feature | defer | async |
|---|---|---|
| Downloads in parallel | Yes | Yes |
| Executes after HTML parsing | Yes | Not guaranteed |
| Preserves order between scripts | Yes | No |
| Safe for DOM-dependent app code | Usually yes | Usually no |
End of <body> vs defer
| Option | Advantage | Drawback |
|---|---|---|
End of <body> | Simple and widely supported | Scripts are discovered later, so download may start later |
defer in <head> | Early download + delayed execution | Requires understanding of script attributes |
Quick recommendation
- Most app code:
defer - Independent third-party code:
async - Simple traditional pages: end of
<body>is still fine
Cheat Sheet
Quick rules
- A normal
<script>blocks HTML parsing while it loads and runs. - Scripts that touch DOM elements must run after those elements exist.
- For most modern pages, use:
<script src="app.js" defer></script>
- If you do not use
defer, place scripts near the end of<body>. - Use
asynconly when script order does not matter.
Common patterns
Modern default
<head>
<script src="app.js" defer></script>
</head>
Traditional default
<body>
<!-- page content -->
<script src="app.js"></script>
</body>
jQuery with dependency order
<head>
<script src="jquery.js" defer></script>
<script src="app.js" defer></script>
</head>
Important rules
deferpreserves order.asyncdoes not preserve order.- Do not use
asyncfor jQuery and code that depends on jQuery. - Inline event handlers like
onclickare usually best avoided.
Edge case
If a script must run immediately before page parsing continues, a normal <script> may be appropriate, but this is less common for app code.
FAQ
Should script tags go in the head or body?
For most modern application code, put them in the <head> with defer. Without defer, placing them near the end of <body> is a common choice.
Is putting scripts in the head always bad?
No. It is only a problem when scripts block parsing or run before needed DOM elements exist. Using defer solves this in many cases.
Is the end of the body still a good place for scripts?
Yes. It is still a valid and simple pattern, especially for traditional pages or small projects.
When should I use async instead of defer?
Use async for scripts that are independent and do not rely on order, such as analytics or ads.
Why does my JavaScript say an element is null?
Your script probably ran before the browser parsed that element. Use defer, move the script lower, or wait for DOM ready.
Do I still need $(document).ready() with deferred scripts?
Often no, because deferred scripts run after HTML parsing. In older jQuery codebases, however, you will still see it frequently.
Which script should come first: jQuery or my code?
jQuery must load before any script that uses it.
Should I put JavaScript inside HTML attributes like onclick?
Usually no. It is cleaner and easier to maintain if you attach event listeners in JavaScript files.
Mini Project
Description
Build a small page that updates text and attaches a button click handler without failing due to script timing. This demonstrates correct script placement and shows how DOM-dependent JavaScript should be loaded in a practical page.
Goal
Create an HTML page where JavaScript safely updates page content and responds to a button click using a deferred script.
Requirements
- Create an HTML page with a heading, a paragraph, and a button.
- Load the JavaScript from an external file.
- Use a script-loading approach that avoids running code before the DOM is parsed.
- Change the paragraph text when the page loads.
- Show an alert or change text when the button is clicked.
Keep learning
Related questions
Add a Favicon to a Static HTML Page
Learn how to add a favicon to static HTML pages, use correct paths and icon formats, and troubleshoot browser caching and local-file issues.
Allow Only Numeric Input (0-9) in HTML Input Using jQuery
Learn how to allow only digits 0-9 in an HTML input using jQuery, with examples, validation tips, common mistakes, and best practices.
Apply CSS !important with jQuery .css()
Learn why jQuery .css() cannot set !important and how to use CSSStyleDeclaration.setProperty() to apply inline important styles safely.