Question

Where to Put Script Tags in HTML: Head vs Body and Best Practices

htmljavascript

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 using defer and want scripts to run after the page markup.
  • Use async only 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
Take Quiz

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

  1. The browser starts reading the HTML document.
  2. It reaches <script src="app.js" defer></script> in the <head>.
  3. Because of defer, the browser starts downloading app.js but does not stop parsing the HTML.
  4. The browser continues into the <body>.
  5. It creates the <p id="status"> element in the DOM.
  6. HTML parsing finishes.
  7. The browser executes app.js.
  8. document.getElementById("status") finds the paragraph element.
  9. The text changes from Waiting... to Ready.

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>
Take Quiz

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 defer for most application logic.
  • Use async only when order does not matter.
  • Avoid inline event handlers like onclick when 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
  • el is null

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.js may 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.js may 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

ApproachWhere placedBlocks HTML parsing?Keeps execution order?Good for
Normal <script><head> or <body>YesYesSmall scripts that must run immediately
<script> at end of <body>End of bodyLess harmful to renderingYesTraditional DOM-dependent scripts
<script defer>Usually <head>No during parsingYesMost application code
<script async>Usually <head>No during parsingNoAnalytics, ads, independent third-party scripts

defer vs async

Featuredeferasync
Downloads in parallelYesYes
Executes after HTML parsingYesNot guaranteed
Preserves order between scriptsYesNo
Safe for DOM-dependent app codeUsually yesUsually no

End of <body> vs defer

OptionAdvantageDrawback
End of <body>Simple and widely supportedScripts are discovered later, so download may start later
defer in <head>Early download + delayed executionRequires understanding of script attributes

Quick recommendation

  • Most app code: defer
  • Independent third-party code: async
  • Simple traditional pages: end of <body> is still fine
Take Quiz

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 async only 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

  • defer preserves order.
  • async does not preserve order.
  • Do not use async for jQuery and code that depends on jQuery.
  • Inline event handlers like onclick are 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.

Take Quiz

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

  1. Create an HTML page with a heading, a paragraph, and a button.
  2. Load the JavaScript from an external file.
  3. Use a script-loading approach that avoids running code before the DOM is parsed.
  4. Change the paragraph text when the page loads.
  5. Show an alert or change text when the button is clicked.
Take Quiz

Keep learning

Related questions

View more