Cannot use import statement outside a module [React TypeScript Error Solved]

When building modern web applications with React and TypeScript, you may encounter the dreaded "Cannot use import statement outside a module" error. This occurs when you try to use an ES module import in a file that TypeScript doesn‘t recognize as a module.

In this comprehensive guide, we‘ll explore what causes this error and provide multiple solutions you can use to resolve it in your React TypeScript projects. We‘ll also take a deeper look at the differences between ES modules and CommonJS modules and explain how TypeScript handles them.

By the end of this article, you‘ll have a solid understanding of modules in TypeScript and the tools you need to fix "Cannot use import statement outside a module" in a variety of scenarios.

What Does "Cannot use import statement outside a module" Mean?

The "Cannot use import statement outside a module" error is raised when you use an ES module import statement, like this:

import express from ‘express‘;

in a file that isn‘t considered a module. In TypeScript, whether a file is treated as a module or not depends on your tsconfig.json settings and the file extension.

If TypeScript doesn‘t recognize the file as a module, either because of your configuration or because it has a .js extension rather than .ts or .tsx, you‘ll see this error when trying to use import.

This commonly occurs in a few scenarios:

  • When using ES module syntax in a TypeScript project with the wrong module setting in tsconfig.json
  • When writing client-side JavaScript and forgetting to add type="module" to your script tags
  • When using ES module syntax in a Node.js project without specifying "type": "module" in package.json

Let‘s look at some code examples that would trigger this error.

TypeScript Example

Imagine you have an Express server written in TypeScript that looks like this:

import express from ‘express‘;

const app = express();

app.get(‘/‘, (req, res) => {
  res.send(‘Hello world!‘);
});

app.listen(3000, () => {
  console.log(‘Server is running on port 3000‘);
});

If you try to run this code, you‘ll see the "Cannot use import statement outside a module" error. The issue is that TypeScript doesn‘t recognize this as a module file.

JavaScript Example

The same issue can occur in plain JavaScript files if TypeScript is involved in your build process. For example:

// math.js
export function add(a, b) {
  return a + b;
}
<!-- index.html -->
<script src="math.js"></script>
<script>
import { add } from ‘./math.js‘;

console.log(add(3, 5)); 
</script>

Here the import statement in the inline script will fail with "Cannot use import statement outside a module" because the browser doesn‘t recognize the script as a module.

How to Fix "Cannot use import statement outside a module" in TypeScript

To fix the error in TypeScript files, you need to configure TypeScript to treat your files as modules. The easiest way to do this is to change the module setting in your tsconfig.json file.

By default, a new tsconfig.json file created with tsc –init will have a module setting of "commonjs". This means TypeScript will treat your files as CommonJS modules, which don‘t support import statements.

To enable ES module imports, change the module setting to "es2015" or later:

{
  "compilerOptions": {
    "module": "es2015",
    // ...
  }
}

With this configuration, TypeScript will recognize files with a .ts or .tsx extension as ES modules and allow you to use import and export statements.

If you‘re working on a Node.js project and want to use ES modules with a .js extension, you also need to add "type": "module" to your package.json file:

{
  "name": "my-app",
  "type": "module",
  // ...  
}

This tells Node.js to treat .js files as ES modules.

Fixing "Cannot use import statement outside a module" in Client-Side JavaScript

For client-side JavaScript loaded in a browser, you need to indicate that a script should be treated as a module by adding the type="module" attribute to the script tag:

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

This tells the browser that the script is an ES module and allows you to use import and export statements within it.

If you inline your JavaScript within a script tag, you also need to add type="module":

<script type="module">
import { add } from ‘./math.js‘;

console.log(add(3, 5));
</script>

Without type="module", the browser will raise the "Cannot use import statement outside a module" error when it encounters the import statement.

Understanding Modules in JavaScript and TypeScript

To fully grasp why the "Cannot use import statement outside a module" error occurs and how to avoid it, it helps to understand the difference between ES modules and CommonJS modules and how TypeScript handles them.

ES Modules vs CommonJS Modules

ES modules (or ECMAScript modules) are the official standard format for packaging JavaScript code for reuse. They were introduced in ES2015 and are now supported by all modern browsers and Node.js.

ES modules use the import and export keywords to define their public API. Imports are live read-only views of the exported values:

// math.js
export const PI = 3.14;

export function square(x) {
  return x * x;
}
// app.js
import { PI, square } from ‘./math.js‘;

console.log(PI); // 3.14
console.log(square(5)); // 25  

In contrast, CommonJS modules use require() to load dependencies and module.exports or exports to expose values:

// math.js
const PI = 3.14;

function square(x) {
  return x * x;  
}

module.exports = { PI, square };
// app.js
const { PI, square } = require(‘./math.js‘);

console.log(PI); // 3.14
console.log(square(5)); // 25

CommonJS modules were popularized by Node.js before ES modules existed and are still widely used in Node.js projects.

The key differences are:

  • ES modules use import/export, CommonJS uses require/module.exports
  • ES module imports are live views of the exported values, while CommonJS imports are copies of the exported values at the time of import
  • ES modules are always in strict mode, while CommonJS modules are not
  • ES modules are asynchronously loaded and evaluated, while CommonJS modules are synchronously loaded and evaluated

How TypeScript Handles Modules

TypeScript supports both ES modules and CommonJS modules and can compile them to different target formats based on your tsconfig.json settings.

The module setting in tsconfig.json determines which module format TypeScript emits when it compiles your code:

  • "commonjs" (default): TypeScript emits CommonJS module code
  • "es2015" or later: TypeScript emits ES module code
  • "amd", "umd", "system": TypeScript emits code for other module loaders

TypeScript treats .ts and .tsx files as modules by default, which is why you can use top-level import and export statements in these files without any additional configuration.

However, TypeScript treats .js files as "script" files by default, not modules. This means top-level import and export statements aren‘t allowed, unless you explicitly tell TypeScript to treat the file as a module.

You can do this by:

  1. Changing the file extension to .ts or .tsx
  2. Adding a triple-slash directive to the top of the file: /// <reference types="module" />
  3. Adding "type": "module" to your package.json file (for Node.js projects)

Best Practices to Avoid "Cannot use import statement outside a module"

Based on what we‘ve learned, here are some best practices to avoid the "Cannot use import statement outside a module" error in your React TypeScript projects:

  1. Always use .ts or .tsx file extensions for your TypeScript files. This ensures TypeScript treats them as modules without additional configuration.

  2. For TypeScript projects that target ES modules, set the module option in tsconfig.json to "es2015" or later.

  3. For Node.js projects using TypeScript with ES module syntax in .js files, add "type": "module" to your package.json file.

  4. When loading client-side JavaScript modules in a browser, include the type="module" attribute on your script tags.

  5. Be consistent in your use of ES module syntax (import/export) or CommonJS syntax (require/module.exports) within a single project. Mixing the two can lead to confusion and errors.

Conclusion

The "Cannot use import statement outside a module" error is a common issue when using ES module syntax in TypeScript or JavaScript projects. It occurs when you try to use an import statement in a file that isn‘t recognized as a module.

To fix the error in TypeScript, ensure your tsconfig.json file has the module option set to "es2015" or later. For client-side JavaScript, add type="module" to your script tags. And for Node.js projects, add "type": "module" to your package.json file when using ES module syntax in .js files.

Understanding the differences between ES modules and CommonJS modules, and how TypeScript handles them, is key to avoiding this error and using modules effectively in your projects.

By following the best practices outlined in this guide, you can ensure your React TypeScript projects use modules correctly and steer clear of the dreaded "Cannot use import statement outside a module" error.

Happy coding!

Similar Posts