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
<!-- 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
.jsfile. 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 asrcmust 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.
| Attribute | Downloads | Runs | Order kept? |
|---|---|---|---|
| none | Blocks the page | Immediately | Yes |
defer | In parallel | After the HTML is fully read | Yes |
async | In parallel | As soon as it arrives | No |
type="module" | In parallel | After the HTML (deferred by default) | Yes |
- Use
deferfor your own scripts and put them in the<head>. They download early and run when the whole page is ready. - Use
asyncfor independent scripts that don't depend on anything else, such as analytics. - The older approach, a plain
<script>just before</body>, also works, butdeferin the head starts the download sooner.
Modules and noscript
<script type="module" src="main.js">lets the file useimportandexportto 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
asyncordefer, and consider theintegrityattribute 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.
deferdownloads in parallel and runs after the HTML, in order: the best default.asyncruns 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.
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>Line-by-line explanation
- Line 3The script comes after the elements it changes, so they already exist.
- Lines 4 to 5Work out a greeting from the current hour.
- Lines 6 to 7Update the page's text.
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>Line-by-line explanation
- Lines 2 to 3This script runs before the browser has read the div, so it finds nothing (null).
- Lines 6 to 8This one runs after the div exists, so it finds it.
deferavoids the problem by running scripts after the whole page is read.
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>Line-by-line explanation
- Line 1Placeholder text that the script replaces.
- Line 2Shown only when scripts can't run, with a useful alternative.
- 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
<head>
<script src="js/big-app.js"></script>
</head><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
<script src="app.js">
console.log('This never runs');
</script><script src="app.js"></script>
<script>console.log('This runs');</script>async for scripts that depend on each other
<script src="library.js" async></script>
<script src="uses-library.js" async></script><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.
Load the script without blocking the page and keep its order.
<script src="js/menu.js" ></script>
defer downloads in parallel and runs after the HTML is read.Which attribute loads an external JavaScript file?
<script src="app.js">.What's the main difference between async and defer?
When is the content inside noscript shown?
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>- The paragraph was updated
- No errors
HTML Script Tag Quiz
5 questions to check what you learned.
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
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
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
Which is a good fit for async?
- A script that needs a library loaded first
- A module
- Inline code
- An independent analytics script
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:
- A
<p id="hello">with the text Loading... - A
<noscript>element with a message - A script after the paragraph that sets its text to
Welcome back!
- The paragraph says Welcome back!
- There is a noscript element
- No errors