Adding JavaScript to HTML

Add JavaScript to a page correctly with the script element, choose between inline and external scripts, load them without slowing the page using defer, async and modules, and provide noscript fallbacks.

Lesson 35 of 44Intermediate15 min read

HTML describes content, CSS styles it, and JavaScript makes it interactive: menus that open, forms that check themselves, maps that move, carts that update without reloading. All JavaScript reaches a page through one element: <script>.

How you add a script matters more than most beginners expect. A script in the wrong place, or loaded the wrong way, can stop the page from appearing until it has downloaded and run, which makes sites feel slow, especially on mobile networks. A script that runs too early can't find the elements it wants to change, because the browser hasn't read them yet.

In this lesson you'll learn inline and external scripts, why the defer attribute is usually the right default, when async fits better, how type="module" works, and what <noscript> is for. It's the HTML side of the story; the JavaScript tutorial teaches the language itself.

Inline and external scripts

HTML
<!-- external: the usual choice -->
<script src="js/app.js" defer></script>

<!-- inline: small snippets only -->
<script>
  console.log('Page loaded');
</script>
  • An external script lives in its own .js file. Browsers cache it, so it downloads once for the whole site, and the code is easier to maintain.
  • An inline script sits directly in the HTML. Use it for tiny snippets such as configuration.
  • A <script> with a src must be empty: code between its tags is ignored.
  • You don't need type="text/javascript"; JavaScript is the default.

defer, async and where to put scripts

By default, when the browser meets <script src>, it stops building the page, downloads the script, runs it, then carries on. That's called parser blocking.

AttributeDownloadsRunsOrder kept?
noneBlocks the pageImmediatelyYes
deferIn parallelAfter the HTML is fully readYes
asyncIn parallelAs soon as it arrivesNo
type="module"In parallelAfter the HTML (deferred by default)Yes
  • Use defer for your own scripts and put them in the <head>. They download early and run when the whole page is ready.
  • Use async for independent scripts that don't depend on anything else, such as analytics.
  • The older approach, a plain <script> just before </body>, also works, but defer in the head starts the download sooner.

Modules and noscript

  • <script type="module" src="main.js"> lets the file use import and export to split code into modules. Modules are deferred automatically and run in strict mode.
  • <noscript> holds content shown only when JavaScript is turned off or blocked, such as "This calculator needs JavaScript" or a plain link to an alternative page.
  • Third-party scripts (chat widgets, analytics, adverts) run with full access to your page. Load only what you need, add them with async or defer, and consider the integrity attribute with a hash for files loaded from public CDNs.

HTML Script Tag Syntax

Script syntax

<head>
  <script src="js/app.js" defer></script>
  <script src="https://example-analytics.com/a.js" async></script>
  <script type="module" src="js/main.js"></script>
</head>
<body>
  <noscript>This page works best with JavaScript turned on.</noscript>
  <script>
    document.body.classList.add('js');
  </script>
</body>

HTML Script Tag Summary

  • <script src="..."> loads external JavaScript; inline scripts hold code directly.
  • A plain script blocks the page while it downloads and runs.
  • defer downloads in parallel and runs after the HTML, in order: the best default.
  • async runs as soon as it arrives, in any order: good for independent scripts.
  • type="module" enables import and export; <noscript> shows fallback content.

HTML Script Tag Examples

Each example runs in the page. Edit the code and press Run to see what changes.

Example 1

An inline script that changes the page

<h3 id="greeting">Hello!</h3>
<p>It is currently <span id="time">...</span>.</p>
<script>
  const hour = new Date().getHours();
  const word = hour < 12 ? 'Good morning' : hour < 18 ? 'Good afternoon' : 'Good evening';
  document.getElementById('greeting').textContent = word + '!';
  document.getElementById('time').textContent = new Date().toLocaleTimeString();
</script>
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 3The script comes after the elements it changes, so they already exist.
  2. Lines 4 to 5Work out a greeting from the current hour.
  3. Lines 6 to 7Update the page's text.
Example 2

Why order matters

<script>
  const early = document.getElementById('box');
  console.log('Before the element:', early);
</script>
<div id="box">I am the box</div>
<script>
  const late = document.getElementById('box');
  console.log('After the element:', late ? 'found it' : 'missing');
</script>
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 2 to 3This script runs before the browser has read the div, so it finds nothing (null).
  2. Lines 6 to 8This one runs after the div exists, so it finds it. defer avoids the problem by running scripts after the whole page is read.
Example 3

A noscript fallback and a module-style script

<p id="status">Checking your browser...</p>
<noscript><p>JavaScript is turned off. You can still <a href="/contact">contact us by email</a>.</p></noscript>
<script type="module">
  const status = document.getElementById('status');
  status.textContent = 'JavaScript modules are running in this browser.';
</script>
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 1Placeholder text that the script replaces.
  2. Line 2Shown only when scripts can't run, with a useful alternative.
  3. Line 3A module script, deferred automatically, so the paragraph already exists when it runs.

Common HTML Script Tag Mistakes

A blocking script in the head

Don't
<head>
  <script src="js/big-app.js"></script>
</head>
Do
<head>
  <script src="js/big-app.js" defer></script>
</head>

Without defer, the page stays blank until the script has downloaded and run.

Code inside a script with src

Don't
<script src="app.js">
  console.log('This never runs');
</script>
Do
<script src="app.js"></script>
<script>console.log('This runs');</script>

async for scripts that depend on each other

Don't
<script src="library.js" async></script>
<script src="uses-library.js" async></script>
Do
<script src="library.js" defer></script>
<script src="uses-library.js" defer></script>

async scripts can run in any order, so the second may run before the library exists.

HTML Script Tag in Real Projects

Scripts on production sites

Frameworks and build tools generate <script type="module"> tags with defer-like loading, and performance audits such as Lighthouse flag "render-blocking resources" when scripts lack defer or async. Analytics and advert tags are added with async, and cookie-consent tools delay third-party scripts until visitors agree.

HTML Script Tag Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Load the script without blocking the page and keep its order.

<script src="js/menu.js" ></script>
Multiple choiceeasy

Which attribute loads an external JavaScript file?

Multiple choicemedium

What's the main difference between async and defer?

Multiple choicemedium

When is the content inside noscript shown?

Fix the bugmedium

The script runs before the paragraph exists and can't find it. Fix it by moving the script after the paragraph.

<script>
  document.getElementById('msg').textContent = 'Updated by JavaScript';
</script>
<p id="msg">Original text</p>
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • The paragraph was updated
  • No errors

HTML Script Tag Quiz

5 questions to check what you learned.

  1. What does a plain script tag without defer or async do while it loads?

    • Blocks the browser from building the rest of the page
    • Nothing
    • Loads in the background
    • Runs last
  2. Which kind of script can use import and export?

    • Any inline script
    • A script with type="module"
    • A script with async
    • Only scripts in the body
  3. Why prefer external script files for most code?

    • They're cached by the browser and easier to maintain
    • They run faster than inline code
    • Inline scripts are invalid
    • They can't be blocked
  4. Which is a good fit for async?

    • A script that needs a library loaded first
    • A module
    • Inline code
    • An independent analytics script
  5. What is progressive enhancement?

    • Loading scripts gradually
    • Upgrading browsers
    • Building a working page in HTML first, then adding JavaScript improvements on top
    • Using only modules

HTML Script Tag Challenge

A greeting with a fallback

Build:

  1. A <p id="hello"> with the text Loading...
  2. A <noscript> element with a message
  3. A script after the paragraph that sets its text to Welcome back!
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • The paragraph says Welcome back!
  • There is a noscript element
  • No errors