Use ES modules (import/export)
Use native ES module syntax for imports and exports instead of CommonJS require() to enable static analysis, tree-shaking, and better tooling support.
- Use import/export syntax instead of require()/module.exports
- Named exports improve IDE autocompletion and enable tree-shaking
- Add type="module" to script tags or use a bundler to load ES modules in browsers
- Default exports are fine but named exports scale better in large codebases
Rule Details
ES modules (import/export) are the JavaScript standard for code organization and are supported natively in all modern browsers and Node.js.
Code Example
// ❌ CommonJS (avoid for new code)
const utils = require('./utils')
const { formatDate } = require('./utils')
module.exports = { myFunction }
module.exports = myFunction
// ✅ ES Modules
import utils from './utils.js'
import { formatDate } from './utils.js'
export { myFunction }
export default myFunctionWhy It Matters
ES modules are statically analyzable — bundlers can determine at build time what code is actually used and eliminate the rest (tree-shaking). CommonJS require() is dynamic and prevents this optimization. ES modules are also the browser-native standard, reducing the need for build-time transformation.
Named vs Default Exports
// utils.js — named exports (preferred for libraries and utilities)
export function formatDate(date) { /* ... */ }
export function formatCurrency(amount, currency) { /* ... */ }
export const MAX_ITEMS = 100
// consumer.js
import { formatDate, formatCurrency } from './utils.js'
// Only imported functions are included in the bundle// UserCard.js — default export (common for single-concept modules)
export default function UserCard({ user }) { /* ... */ }
// consumer.js
import UserCard from './UserCard.js'
import MyCard from './UserCard.js' // Can rename default importsIn the Browser
<!-- Add type="module" to use import/export directly -->
<script type="module">
import { formatDate } from './utils.js'
console.log(formatDate(new Date()))
</script>Module scripts are deferred by default, execute in strict mode, and have their own scope (no global leakage).
Dynamic Imports for Code Splitting
// Load a heavy module only when needed
async function loadChart() {
const { Chart } = await import('./chart.js')
return new Chart(document.getElementById('canvas'))
}
button.addEventListener('click', loadChart)Re-exporting (Barrel Files)
// index.js — re-export from a central entry point
export { formatDate, formatCurrency } from './date-utils.js'
export { validateEmail, validatePhone } from './validators.js'
export { default as UserCard } from './UserCard.js'Standards
- Use MDN: JavaScript Guide as the standard for how this JavaScript pattern should behave in production, not just in a small local example.
- Use web.dev: Learn JavaScript as the standard for how this JavaScript pattern should behave in production, not just in a small local example.
Support Notes
- Module loading behavior depends on the target browser matrix and build pipeline, so verify the final shipped output rather than relying only on source syntax.
- If legacy targets remain in scope, document the fallback or transpilation path explicitly.
- ES modules and class bodies run in strict mode automatically. If the codebase still ships classic non-module scripts, an explicit
'use strict'directive can still matter there.
Verification
Automated Checks
- Verify the behavior in the browser after the code change, not only in static analysis.
- Inspect DevTools Network or Performance panels when the rule affects loading or execution order.
- Test the primary user flow and one edge case triggered by the changed script path.
Manual Checks
- Confirm the code still behaves correctly when the feature is delayed, lazy-loaded, or fails.
Use with AI
Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.
Check
Verify implementation
Identify any use of require(), module.exports, or exports in this JavaScript file that should be converted to ES module syntax.
Fix
Auto-fix issues
Convert all require() and module.exports statements to ES module import/export syntax.
Explain
Learn more
Explain the benefits of ES modules over CommonJS, including tree-shaking, static analysis, and browser support.
Review
Code review
Review scripts, client components, and browser execution paths related to Use ES modules (import/export). Flag exact imports, event handlers, runtime side effects, or blocking operations that violate the rule, and state how the change should be verified in the browser.