Angular Uncaught SyntaxError Cannot Use Import Statement Outside A Module | Clean Fix Steps

Angular “Cannot use import statement outside a module” shows up when code with ES imports runs as a classic script or CommonJS file.

You see this error, refresh, and the page freezes. Angular is trying to run code that contains import or export, yet the runtime is treating that file as a plain script. Plain scripts can’t parse module syntax, so the browser (or Node) stops right at the first import.

Make the runtime treat that file as a module, or just stop feeding module syntax to a runtime that expects something else.

Why This Error Happens In Angular Apps

Angular projects use the word “module” in a few ways. ES modules are the JavaScript feature that uses import and export. Angular “NgModules” are an Angular concept. Your error is about ES modules, not Angular NgModules.

Module Vs Script In One Minute

In the browser, a file runs in one of two modes. A classic script runs with . A module runs with . A module can use import. A classic script can’t.

Angular CLI normally bundles your app. When this error appears, something is bypassing that step: a scripts entry, a Node run, or a test runner.

What The Console Line Is Telling You

Check the exact file path in the console. If it points to node_modules, you’re likely loading an ESM build of a library as a classic script. If it points to a server file, you’re in a Node module-type mismatch. If it points to a test file, your test transform is missing.

Angular Uncaught SyntaxError Cannot Use Import Statement Outside A Module

Use one rule. Fix the mode mismatch where the file is first executed. Don’t patch it by swapping random syntax.

Fast Triage Checklist

  • Read The File Path — Copy the path shown in DevTools and note if it is a browser asset, a Node script, or a test file.
  • Spot The First import — Open that file and confirm the first import line is where execution stops.
  • Confirm How It Is Loaded — Find the place that loads it: an HTML script tag, an angular.json scripts entry, a Node command, or a Jest config.

Map The Symptom To The Right Fix

The same text can show up in three places: the browser, Node, or a test runner.

Where You See It What It Means Most Likely Fix
Browser DevTools A file with ES imports was loaded as a classic script Stop using angular.json scripts for ESM, import it in TS, or load a UMD build
Node (SSR, tooling) Node treated the file as CommonJS Switch to ESM with "type":"module", use .mjs, or change the entry to CommonJS syntax
Jest / unit tests Jest executed ESM from your app or deps without transforming it Adjust transforms and ignore patterns, especially for Angular ESM packages

Fix It When The Browser Throws The Error

If the error shows in Chrome DevTools, you are often loading a raw library file as a classic script. Importing through TypeScript avoids that.

Stop Loading ESM Through angular.json Scripts

The scripts array in angular.json injects files into index.html as classic scripts. If you point it at an ESM file (many packages ship .mjs or *.esm.js), the browser hits the first import and throws.

  • Remove The scripts Entry — Delete the library path from architect.build.options.scripts (and from test targets if present).
  • Import From Your Code — Install the package and import it in a component or service so the bundler owns it.
  • Pick The Right Build — If the vendor offers a UMD build, load that as a script, not the ESM source build.

Check Your index.html Script Tags

If you added a custom script tag for a file that uses ES imports, set it up as a module. Classic scripts can’t parse imports.

  • Use type="module" — Change the tag to if you truly need a direct browser module.
  • Keep Paths Same-Origin — Module scripts follow stricter loading rules, so serve them from the same site.
  • Prefer Bundled Imports — In Angular, importing in TS is smoother than hand-editing script tags.

When A CDN Example Breaks In Angular

Many library docs show “paste this CDN script” snippets. Some of those snippets point at ESM builds meant for module loaders, not classic script tags. If you copied a link that ends in .mjs or includes esm in the filename, that’s a red flag.

  • Switch To An npm Install — Install the package and import it in your Angular code.
  • Use The UMD Link — If you must use a CDN, pick the UMD build and keep it in scripts.
  • Verify The Global Name — UMD builds expose a global object; match the docs for that build, not the ESM docs.

Fix A Package File Path Mix-Up

If the console path points to something like node_modules/pkg/src/, you’re loading the source file, not the distributed build. Source files often keep raw import lines, while the distributed build is the one meant without extra setup.

  • Switch To The dist File — If you must reference a file path, use the vendor’s documented dist entry, not a src path.
  • Prefer The Package Entry — Import from the package name and let the bundler pick the right build from the package metadata.
  • Rebuild After The Change — Stop the dev server, delete build output, then run ng serve again.

Fix It When Node Or SSR Throws The Error

Sometimes the stack trace is from Node, not the browser. This can happen with Angular SSR, custom build scripts, or tooling that runs TypeScript output under Node. The message means Node is reading a file as CommonJS, then meeting an ES import.

Decide If The Server Side Should Be ESM Or CommonJS

Pick one mode and align your package settings, build output, and run command.

  • Stay On CommonJS — Use require() in Node entry files and set your TS output module type to CommonJS for that target.
  • Move To ESM — Use "type": "module" in package.json or rename the entry to .mjs, then keep import in server code.
  • Split By Extension — Use .cjs for CommonJS files and .mjs for ESM when you must mix.

Common Angular SSR Slip-Ups

SSR setups involve a server entry file and a build output folder.

  • Run The Built Server File — Point Node at the compiled output, not the TypeScript source, unless you are using a runner that transpiles.
  • Match The Node Version — Use a Node release that matches your Angular tooling and handles modern ESM features.
  • Watch For Tooling Flags — If your run command still passes flags meant for older module handling, clean them up and rely on current settings.

tsconfig Settings That Often Matter

TypeScript controls what module format your output uses. If Node is executing the output, this setting can decide whether import remains or gets rewritten.

  • Check compilerOptions.module — Use a module setting that matches your chosen mode, such as ES2022 for ESM or CommonJS for CommonJS targets.
  • Check moduleResolution — Newer Angular projects may use bundler-style resolution for modern builds; keep it aligned across app and server configs.
  • Keep One Source Of Truth — If you have multiple tsconfig files, confirm the one used by the server build is the one you edited.

Fix It In Jest And Other Test Runners

When the error appears during ng test or a Jest run, the runtime is usually trying to execute ESM from node_modules without transforming it. Angular packages and many modern libs ship ESM first. Jest can handle that, but only when transforms are set up for it.

Confirm Which Runner You Are Using

Angular projects may use Karma or Jest. Start by checking which tool runs tests in your repo.

  • Check package.json Scripts — See if test runs ng test, jest, or another tool.
  • Find The Config File — Look for jest.config.*, karma.conf.js, or a builder entry in angular.json.
  • Match The Stack Trace — The stack trace usually names the tool that tried to execute the file.

Jest: Let It Transform ESM Dependencies

Many Jest configs skip transforms for node_modules. That breaks when a dependency ships ESM only. Allow transforms for selected packages.

  • Adjust transformIgnorePatterns — Whitelist the Angular and third-party packages that ship ESM so Jest can process them.
  • Use The Preset Defaults — Start from a known working jest-preset-angular config, then layer changes one by one.
  • Rebuild The Cache — Clear Jest cache after config changes so old transforms don’t linger.

Karma: Avoid ESM Script Injection

If you use Karma and you manually add scripts, you can create the same classic-script problem you saw in the browser. Prefer importing libraries in TypeScript so the test bundler can process them.

  • Remove Raw Script Adds — Drop direct ESM file adds from test configs.
  • Import In Test Files — Import the library in the spec or in a shared test helper.
  • Check Polyfill Lists — If a polyfill file was replaced with an ESM build by mistake, restore the expected file.

Fixing Cannot Use Import Statement Outside A Module In Angular Builds

After it’s fixed, reduce the chance it returns after a dependency bump.

Replace File Paths With Package Imports

Direct file paths into node_modules are fragile. Package imports give your bundler room to pick the right format for the current build.

  • Import From The Package Root — Use import ... from "package-name" instead of hard-coding a file in node_modules.
  • Use Angular Builders — Let the Angular build pipeline copy assets and bundle code instead of hand-adding scripts.
  • Audit angular.json — Keep scripts for true non-module globals only.

Use A Simple Pre-Commit Check

A tiny check can catch a broken script injection early.

  • Search For .mjs In scripts — If a path in angular.json scripts ends with .mjs, treat it as a bug to fix.
  • Search For esm In Script Paths — Many vendors label ESM builds with esm in filenames.
  • Run A Clean Build — Delete the output folder and rebuild to be sure you are testing fresh artifacts.

Two Lines To Share With Team-Mates

Share this model: “Find the file that contains the first import, then make sure it is executed as an ES module or bundled by Angular.”

When the message reads angular uncaught syntaxerror cannot use import statement outside a module, the fix is nearly always a script-mode mismatch, not a broken Angular import in your app code.

If you keep seeing angular uncaught syntaxerror cannot use import statement outside a module after changes, paste the file path into your editor, trace who loads it, and remove that raw load path first.