TypeScript error

TS2835: A relative import without its file extension

  • Error TS2835
  • Tested with TypeScript 5.9

error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './format.js'? is reported for import ... from "./format" in an ES module under nodenext resolution. Node.js's ES module loader does not guess extensions or look for index.js, so the import would fail when the code runs.

TS2834 is the same rule when the checker has no single suggestion, typically a folder import such as "./utils", which Node.js would need written as "./utils/index.js".

At a glance

CodeTS2835
MessageRelative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean '{0}'?
Also on this page: TS2834 Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Consider adding an extension to the import path.
TypeScript's categoryerror
Listed underModules and imports
Depends on moduleResolution, module
In .js files Reported when the file is checked: checkJs in jsconfig.json, or // @ts-check at the top of the file

The messages are TypeScript's own text from its diagnostic messages file (Apache-2.0); {0} is filled in by tsc for each error.

What it looks like

app.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: app.js(2,29): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './format.js'? app.js(4,25): error TS2834: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Consider adding an extension to the import path. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. app.js(2,29): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './format.js'? app.js(4,25): error TS2834: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Consider adding an extension to the import path. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. app.js(2,29): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './format.js'? app.js(4,25): error TS2834: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Consider adding an extension to the import path. checked 21 October 2026
import { formatPrice } from "./format";error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './format.js'?
import { slugify } from "./utils";error TS2834: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Consider adding an extension to the import path.

export const label = `${slugify("Cotton tee")}: ${formatPrice(1999)}`;
Relative imports without the file extension under nodenext.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["**/*.js"]
}

What causes it

  • The file is an ES module (a .js file in a package with "type": "module", or a .mjs file) and moduleResolution is node16 or nodenext.
  • The import leaves out .js, or points at a folder and relies on index.js, habits from CommonJS and bundlers.

How to fix it

Write the path Node.js resolves

Add the extension, ./format.js, and spell out folder imports, ./utils/index.js. In a JavaScript project the path is simply the real file name.

app.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
import { formatPrice } from "./format.js";
import { slugify } from "./utils/index.js";

export const label = `${slugify("Cotton tee")}: ${formatPrice(1999)}`;
The paths Node.js resolves, extension and all.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["**/*.js"]
}

Or, if a bundler resolves your imports, use bundler resolution

Code that only ever runs through Vite, webpack or another bundler can keep extensionless imports with "module": "preserve" and "moduleResolution": "bundler". Do not use this for code Node.js runs directly.

app.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
import { formatPrice } from "./format";
import { slugify } from "./utils";

export const label = `${slugify("Cotton tee")}: ${formatPrice(1999)}`;
moduleResolution bundler, for a bundler that resolves imports without extensions.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "preserve",
    "types": [],
    "moduleResolution": "bundler"
  },
  "include": ["**/*.js"]
}

Gotchas

  • CommonJS files under nodenext (a package without "type": "module") may still import without extensions, because require resolution adds them; only ES modules are held to the rule.
  • A path with the .js extension works under every moduleResolution mode, bundler included, so writing extensions everywhere keeps code portable between the two.

Sources

Last reviewed 20 October 2026.