Question
Understanding the 'React' Refers to a UMD Global Error in TypeScript
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.
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.
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 need to import React just for JSX:
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:
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.
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.
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 = () => {
[count, setCount] = ();
;
};
Comparisons
| Topic | Older approach | Newer approach |
|---|---|---|
| JSX transform | Requires React in scope | Can auto-use JSX runtime |
tsconfig.json JSX option | "jsx": "react" | "jsx": "react-jsx" |
| Component file with only JSX | Usually needs import React from 'react' | Usually does not need it |
Importing hooks like useState | Still required | Still required |
| Compatibility | Works with older setups | Best for modern React tooling |
jsx: react vs jsx: react-jsx
Cheat Sheet
Quick rules
- JSX in older setups needs
Reactin scope. - JSX in modern setups can work without importing
React. - TypeScript decides this based on
compilerOptions.jsx. exportorimportmakes 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/reactversiontsconfig.json- editor TypeScript version
Still import from React when needed
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?
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
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.
Angular @ViewChild Returning Undefined: Lifecycle, Child Components, and Fixes
Learn why Angular @ViewChild can be undefined, when it becomes available, and how to access child components correctly using lifecycle hooks.