
ES Module Import Syntax Change: From assert
to with
If you’re reading this, chances are you have encountered this syntax in your project or an error message associated to using assert
. Today’s post delves into a change in how we import JSON files using ES Modules (ESM) in Node.js, the transition from assert
keyword to with
keyword:
import data from "./data.json" assert { type: "json" }import data from "./data.json" with { type: "json" }
TL;DR The with
keyword is supported from Node.js 18.20.0 onwarwds. If you don’t need to support older version, replace assert
to with
.
This article provides an overview of the syntax change from assert
to with
in ES module imports, including its background, rationale, and compatibility considerations.
assert
Understanding The assert { type: "json" }
syntax was introduced as part of the “Import Assertions” proposal 1. It allowed developers to explicitly specify the expected MIME type when importing JSON modules:
import config from './config.json' assert { type: 'json' };const dynamic = await import('./data.json', { assert: { type: 'json' } });
This feature was designed to enhance security by preventing malicious servers from delivering executable JavaScript code disguised as JSON files. The assertion would terminate the import operation if the module’s actual MIME type did not match application/json
. 2 3 4
with
Keyword
The Shift to The assert
keyword has been officially deprecated and is being replaced with with
as part of the “Import Attributes” proposal. 3 The updated syntax for static imports is:
import config from './config.json' with { type: 'json' };
For dynamic imports, the attributes are now passed as a property within an options object:
import('./config.json', { with: { type: 'json' } });
This change represents more than just a syntactic alteration; it signifies a fundamental shift in how these declarations operate. 5 The transition from assert
to with
was driven by the identification of incompatibilities between the original design and established web architecture. 3
Rationale for the Change
The original assert
keyword had several limitations:
- It could not modify HTTP request headers (e.g., sending an
Accept: application/json
header) to explicitly request a specific content type from the server. - This meant the client’s expectation regarding the content type was not communicated during the initial fetch operation.
- The type of resource being requested dictates which Content Security Policies (CSPs) are applied, and because
assert
lacked the ability to influence the fetch behavior, it could not correctly interact with the web’s security model.
To address these deficiencies, “Import Attributes” (with
) were developed with more flexible semantics that allow them to directly influence how a module is loaded, including generating appropriate HTTP request headers and ensuring proper alignment with web security policies.
Node.js Version Compatibility
The following table details Node.js version support for import... assert
versus import... with
:
Node.js Version Range | assert Support | with Support |
---|---|---|
16.14.0 - 17.x | Yes * | No |
18.0.0 - 18.19.x | Yes | No |
18.20.0 - 21.x | Yes (Deprecated) | Yes |
22.0.0+ | No (Removed) | Yes |
* (Experimental, --experimental-json-modules
flag needed initially) 6
Key Compatibility Notes
- Node.js versions 16.14.0 and 17.1.0 were the first to introduce experimental support for
import assertions
. 6 - Node.js version 18.20.0 added support for
import attributes
, facilitating migration. 7 - Node.js version 22.0.0 and later have removed support for
assert
in favor ofwith
. 8
This created an overlap period where certain Node.js versions might have recognized both syntaxes.
gantt dateFormat YYYY-MM-DD title Node.js Import Syntax Evolution: assert vs. with section Import Syntax Introduction Node.js 16.14.0 (assert experimental) : milestone, 2022-02-08, 1d Node.js 17.1.0 (assert experimental) : milestone, 2021-11-04, 1d Node.js 18.20.0 (with introduced) : milestone, 2024-03-19, 1d Node.js 20.10.0 (with supported) : milestone, 2023-11-23, 1d section Syntax Support Periods assert syntax supported (16.x & 17.x) :done, 2021-11-04, 2024-04-22 assert syntax supported (18.x) :done, 2022-04-19, 2025-04-30 with syntax supported (18.20.0+) : 2024-03-19, 2025-04-30 with syntax supported (20.10.0+) : 2023-11-23, 2026-04-30 with syntax supported (22.x+) : 2024-04-23, 2027-04-30 section Key Compatibility Changes Node.js 18.x LTS (EOL) :crit, 2025-04-30, 0.5d Node.js 22.0.0 (assert removed) :crit, 2024-04-23, 0.5d
A critical point exists regarding compatibility: Node.js 18.x versions prior to 18.20.0 did not support the with
syntax. This means that if code is changed to immediately adopt the with
syntax, Node.js 18.x versions prior to 18.20.0 will produce an error: 9 10
SyntaxError: Unexpected identifier 'with' at ESMLoader.moduleStrategy (node:internal/modules/esm/translators:117:18) at ESMLoader.moduleProvider (node:internal/modules/esm/loader:361:14) at async link (node:internal/modules/esm/module_job:70:21)
Node.js v17.9.1
Conversely, continuing to use the assert
syntax will cause errors in Node.js 22.x and later versions:
SyntaxError: Unexpected identifier 'assert' at compileSourceTextModule (node:internal/modules/esm/utils:344:16) at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:105:18) at #translate (node:internal/modules/esm/loader:534:12) at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:581:27) at async ModuleJob._link (node:internal/modules/esm/module_job:154:19)
Node.js v22.17.1
Migration Strategies
For projects targeting modern Node.js versions (18.20.0+), the most straightforward path is to replace assert
with with
. However, for projects requiring broader compatibility:
Option 1: Target a Specific Modern Node.js Version
Mandate a minimum Node.js version (e.g., 18.20.0 or later) to simplify migration.
Option 2: Use fs.readFileSync
and JSON.parse
For broadest compatibility, use synchronous file reading with JSON parsing 9:
import { readFileSync } from 'node:fs';const config = JSON.parse(readFileSync(new URL('./config.json', import.meta.url), 'utf8'));
Option 3: Utilize createRequire
for CommonJS-like Loading in ESM
For synchronous loading with caching behavior 9:
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);const config = require('./config.json');
with
Keyword
Future Directions of the Import Attributes, while a syntax feature themselves, lay the foundational groundwork for actual functionalities that leverage this syntax. 4.
CSS Modules
The with
keyword enables native CSS module scripts, allowing the ability to load CSS stylesheets as constructable stylesheets 11, represented by CSSStyleSheet objects, directly into the JavaScript module graph. The syntax mirrors that of JSON modules 12:
import cssModule from "./styles.css" with { type: "css" };document.adoptedStyleSheets = [cssModule];shadowRoot.adoptedStyleSheets = [cssModule];
This feature is landed on Chrome 123. Be sure to check back for support in Firefox and Safari: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import/with#browser_compatibility
Broader Implications
The with
syntax aims to create a more extensible and performant module system for the web. It allows native browser support for various asset types while presenting challenges in standardization, tool adaptation, and backward compatibility.
Conclusion
The assert
syntax is deprecated and will be removed. Developers should migrate to the with
syntax to ensure compatibility with newer Node.js releases.
For projects requiring broad compatibility, consider alternative strategies like using fs.readFileSync
or createRequire
. Always implement robust error handling when importing or parsing JSON.