Question

Understanding the 'React' Refers to a UMD Global Error in TypeScript

typescriptjavascriptreactjscreate-react-app

Question

I upgraded a project to Create React App 4 and am gradually converting files to TypeScript. I understand that with newer React versions, it is often no longer necessary to repeatedly write import React from 'react' in every component file.

However, in my TypeScript files, when I do not import React, I get this error:

'React' refers to a UMD global, but the current file is a module.
Consider adding an import instead. ts(2686)

I know I can remove the error by importing React, but I thought that was no longer required. Why is TypeScript still showing this error, and what does the error message actually mean?

Example TSX file:

const Users = () => {
    return <>Teachers aka Users</>;
};

export default Users;

Short Answer

By the end of this page, you will understand why some React TypeScript projects still ask for a React import, what the "UMD global" error means, how the new JSX transform changes this behavior, and which TypeScript settings or React versions control whether the import is required.

Concept

In older React projects, JSX was transformed into calls like:

React.createElement(...)

Because of that, every file using JSX needed React to be in scope:

import React from 'react';

Starting with the newer JSX transform introduced for React 17+, JSX no longer has to compile to React.createElement(...) in the same way. Tools can instead automatically import the JSX runtime behind the scenes. That is why many modern React files do not need a manual React import.

TypeScript only stops requiring that import when your project is configured to use the newer JSX runtime. If TypeScript is still using the older JSX mode, it assumes JSX depends on the React identifier being available in the file.

The error:

'React' refers to a UMD global, but the current file is a module.

means TypeScript found a global type or value named React, but your file is being treated as a module because it has imports, exports, or module-based syntax. In module-based code, TypeScript prefers explicit imports instead of silently relying on globals.

So the core issue is usually not React itself, but a mismatch between:

  • your React version
  • your TypeScript version
  • your tsconfig.json JSX setting
  • your editor's TypeScript support

This matters in real programming because build tools, editors, and compilers must all agree on how JSX should be interpreted. If one part of the toolchain uses old rules and another uses new rules, you get confusing errors like this one.

Mental Model

Think of JSX like shorthand that needs to be translated before the browser can use it.

Older model

JSX was like writing a nickname that always expanded to:

React.createElement(...)

So TypeScript said: "If you're using JSX, I need React to be present in the file."

Newer model

Now JSX can be translated using a helper that gets imported automatically.

So instead of saying, "Bring your own React name," the compiler says, "I'll wire that up for you."

Why the error happens

Your project is acting like one person updated the instructions, but another person is still following the old manual.

  • React/Cra may support the new transform
  • TypeScript or your config may still expect the old one

That mismatch causes TypeScript to complain.

Take Quiz

Syntax and Examples

The key setting is usually in tsconfig.json.

Old JSX setting

{
  "compilerOptions": {
    "jsx": "react"
  }
}

With this setting, JSX expects React to be in scope:

import React from 'react';

const Users = () => {
  return <>Teachers aka Users</>;
};

export default Users;

New JSX setting

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

With this setting, you usually do not need to import React just for JSX:

const Users = () => {
  return <>Teachers aka Users</>;
};

export default Users;

When you still need to import from React

Even with the new JSX transform, you still import React APIs when you actually use them:

import { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return <button onClick={() => setCount(count + 1)}>{count}</button>;
};

export default Counter;

Here, React itself is not imported, but useState is, because the file uses that function.

Practical fix

If your setup supports the new transform, make sure:

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

If not, keep using:

import React from 'react';

until your TypeScript/tooling configuration is updated.

Step by Step Execution

Consider this component:

const Users = () => {
  return <>Teachers aka Users</>;
};

export default Users;

Step 1: TypeScript sees JSX

The fragment syntax:

<>Teachers aka Users</>

is JSX.

Step 2: TypeScript checks the JSX mode

It reads your tsconfig.json and looks at:

"jsx": "react"

or

"jsx": "react-jsx"

Step 3: If the mode is react

TypeScript assumes JSX needs React in scope. It effectively thinks in terms of code like:

React.createElement(...)

So when there is no import, it complains.

Step 4: Why it mentions a UMD global

Some React type definitions expose a global React name for older script-based usage. TypeScript notices that a global React exists, but your file is a module because it contains:

export default Users;

Modules should use imports, not implicit globals, so TypeScript warns you.

Step 5: If the mode is react-jsx

TypeScript uses the newer JSX runtime and does not require React to be in scope just to render JSX.

That is why the same component works without an import in properly configured modern projects.

Real World Use Cases

This concept appears in many everyday React codebases.

Migrating JavaScript React apps to TypeScript

A project may already build correctly in JavaScript, but once TypeScript is introduced, JSX settings become stricter and reveal config mismatches.

Upgrading React versions

Teams moving from React 16 to React 17+ often expect to remove all import React lines. That only works if TypeScript and the build setup also support the new transform.

Monorepos

One package may use the new JSX runtime while another still uses older TypeScript settings. Components copied between packages can suddenly show this error.

Editor-only problems

Sometimes the app builds fine, but VS Code shows the error because it is using an older TypeScript version than the one in your project.

Shared component libraries

A library may intentionally keep import React for compatibility with multiple consumer projects, even if newer apps do not strictly need it.

Take Quiz

Real Codebase Usage

In real projects, developers handle this in a few common ways.

1. Use the correct tsconfig.json setting

Modern React TypeScript apps commonly use:

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

This is the cleanest solution when your tooling supports it.

2. Keep explicit imports for compatibility

Some teams keep:

import React from 'react';

in every component to avoid issues across older tools, test runners, Storybook setups, or libraries.

3. Import only what you use

A common modern pattern is:

import { useEffect, useState } from 'react';

without importing the default React, unless the project still requires it.

4. Use guard clauses during migration

When migrating gradually, developers often fix config first, then clean up imports later. That avoids editing every file twice.

5. Check editor TypeScript version

In VS Code, a frequent fix is switching the workspace to use the project's installed TypeScript version rather than the editor's bundled one.

6. Align build and type tooling

In production codebases, React, @types/react, TypeScript, Babel, and the build tool should all support the same JSX runtime model. Mismatched versions are a common source of confusion.

Common Mistakes

Mistake 1: Assuming React 17+ automatically fixes everything

Having a newer React version alone is not enough.

You also need compatible:

  • TypeScript version
  • @types/react
  • JSX compiler setting
  • editor/tooling

Mistake 2: Using the old JSX setting

Broken configuration:

{
  "compilerOptions": {
    "jsx": "react"
  }
}

If you want to omit the React import for JSX, use:

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

Mistake 3: Removing all React imports blindly

This can break files that use React APIs.

Broken idea:

const App = () => {
  const [count, setCount] = useState(0);
  return <div>{count}</div>;
};

This fails because useState is not imported.

Correct version:

import { useState } from 'react';

const App = () => {
  const [count, setCount] = useState(0);
  return <div>{count}</div>;
};

Mistake 4: Ignoring editor mismatch

Your app may compile, but your editor may still show the error if it uses a different TypeScript version.

How to avoid it:

  • ensure TypeScript is installed in the project
  • make the editor use the workspace version
  • restart the TypeScript server if needed

Mistake 5: Confusing runtime behavior with type-checking behavior

This error is often from TypeScript analysis, not necessarily from the browser or runtime build. A project can sometimes run while the editor still reports a type/config problem.

Comparisons

TopicOlder approachNewer approach
JSX transformRequires React in scopeCan auto-use JSX runtime
tsconfig.json JSX option"jsx": "react""jsx": "react-jsx"
Component file with only JSXUsually needs import React from 'react'Usually does not need it
Importing hooks like useStateStill requiredStill required
CompatibilityWorks with older setupsBest for modern React tooling

jsx: react vs jsx: react-jsx

// Older
{
  "compilerOptions": {
    "jsx": "react"
  }
}
// Newer
{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

Manual import vs automatic JSX runtime

PatternExampleWhen used
Manual React importimport React from 'react';Older TypeScript/React JSX setup
Automatic runtimeNo default React import needed for JSXModern React 17+ compatible setup
Named importsimport { useEffect } from 'react';Whenever React functions are used
Take Quiz

Cheat Sheet

Quick rules

  • JSX in older setups needs React in scope.
  • JSX in modern setups can work without importing React.
  • TypeScript decides this based on compilerOptions.jsx.
  • export or import makes a file a module.
  • Modules should use explicit imports instead of globals.

Key setting

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

If you see this error

'React' refers to a UMD global, but the current file is a module.

check:

  • React version
  • TypeScript version
  • @types/react version
  • tsconfig.json
  • editor TypeScript version

Still import from React when needed

import { useState, useEffect } from 'react';

Typical fix order

  1. Check tsconfig.json
  2. Confirm modern TypeScript support
  3. Confirm React type packages are up to date
  4. Restart editor/TypeScript server
  5. Add import React from 'react' only if your setup still requires it

FAQ

Why does TypeScript want me to import React if React 17 removed that requirement?

Because the new behavior depends on the JSX transform configuration. If TypeScript is still using the old JSX mode, it will continue expecting React in scope.

What does “UMD global” mean in this error?

It means TypeScript sees React as a global name that may exist in older non-module setups. But your file is a module, so TypeScript wants an explicit import instead of a global reference.

Do I still need to import hooks like useState?

Yes. The new JSX transform only removes the need to import React for JSX itself. Functions like useState, useEffect, and useMemo still need imports.

What tsconfig.json setting should I use for modern React?

Use:

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

if your project tooling supports it.

Why does the app build, but VS Code still shows the error?

Your editor may be using a different TypeScript version from the one your project uses. Switch VS Code to the workspace TypeScript version and restart the TypeScript server.

Is it wrong to keep import React from 'react' in every file?

No. It may be unnecessary in modern setups, but it is still valid and sometimes kept for consistency or compatibility.

Does export default make the file a module?

Yes. Any file with import or export is treated as a module by TypeScript.

Take Quiz

Mini Project

Description

Create a small TypeScript React component file and make it work in both an old-style JSX setup and a modern JSX runtime setup. This helps you see exactly when import React from 'react' is required and when it is not.

Goal

Build a simple Users component and configure TypeScript so the file works without an unnecessary default React import when using the modern JSX transform.

Requirements

  • Create a TSX component that renders a short piece of text using JSX
  • Export the component as the default export
  • Configure TypeScript to use the modern JSX runtime
  • Add a React hook import to show that named imports are still required when used
Take Quiz

Keep learning

Related questions

View more