What you'll be able to do

By the end of this page you should be able to split a tiny app into main.js + helpers, use import / export, load with <script type="module">, and know why file:// sometimes fails (use a local server).

Previous post: localStorage, sessionStorage, and cookies.

Who this is for

  • People with one giant <script> block
  • Beginners who saw import in tutorials and got CORS errors
  • Anyone finishing browser fundamentals before async/fetch phases

You can skip this if you already run Vite or similar. Come back when plain HTML needs one extracted helper file.

export and import

utils.js:

export function greet(name) {
  return "Hello, " + name;
}

main.js:

import { greet } from "./utils.js";
console.log(greet("Asha"));

Scenario A — shared formatters across pages later.

Scenario B — default export — one main thing per file is common:

export default function init() { /* ... */ }
import init from "./app.js";

type="module" in HTML

<script type="module" src="main.js"></script>

Module scripts are defer by default — DOM parsed first. They run in strict mode. Top-level variables do not become global window properties.

Paths matter

./utils.js is relative to the HTML URL (or the module URL). Wrong path → 404 in Network tab.

Scenario A — simple folder:

index.html
main.js
utils.js

Scenario B — opening file:// — some browsers block module imports. Use a tiny local server (npx serve, VS Code Live Server) when imports fail mysteriously.

Classic script vs module (quick)

classic <script>type="module"
Scopeglobal unless wrappedmodule scope
deferno (unless attr)yes by default
import/exportnoyes

A tiny practice file

index.html:

<!DOCTYPE html>
<html>
  <body>
    <p id="out"></p>
    <script type="module" src="main.js"></script>
  </body>
</html>

main.js:

import { greet } from "./utils.js";
document.getElementById("out").textContent = greet("Learn");

utils.js:

export function greet(name) {
  return "Hello, " + name;
}

Serve the folder over http if file:// blocks modules.

Mistakes I see a lot

1. Forgetting .js extension in import path (browser needs it).

2. Missing type="module".

3. Expecting globals from imported files.

4. CORS / file protocol confusion — not “broken JavaScript,” environment issue.

5. Circular imports in tiny demos — split responsibilities.

What to try before the next post

  1. Extract one function to utils.js.
  2. Import it from main.js.
  3. Open DevTools Network if 404.
  4. Run via local server if needed.

This closes Phase 7 (browser JS). Next: callbacks and timing — why setTimeout logs feel “out of order,” and how callbacks set up promises.

Try this next outside the series

Browser JavaScript stops feeling isolated once you connect it to forms and component-oriented UI work.