ES modules let JavaScript files expose values and load them from other files without putting every helper on the global object. That boundary keeps page, package, or small-script helpers reusable when formatters, constants, classes, or data helpers need to move across more than one entry point.
An export declaration makes a top-level binding available outside its module. An import declaration names the exported binding and the module specifier, which is the string that the runtime resolves to another file or package.
Node.js treats .mjs files as ES modules, so the same two-file setup runs from a terminal without a build step. Browser modules use the same static import and export syntax, but the entry script must be loaded as type=“module” and relative specifiers must resolve as URLs from the importing file.
export const roleLabels = { admin: "Administrator", editor: "Editor", }; export default function formatDisplayName(profile) { const role = roleLabels[profile.role] ?? "User"; return `${profile.name} (${role})`; }
The default export gives the module one primary value, and roleLabels remains a named export that must be imported with braces.
import formatDisplayName, { roleLabels } from "./profile-format.mjs"; const profile = { name: "Ada Lovelace", role: "admin", }; console.log(formatDisplayName(profile)); console.log(`Known roles: ${Object.keys(roleLabels).join(", ")}`);
The ./profile-format.mjs specifier is relative to app.mjs. Keep the file extension in native Node.js ES module imports.
$ node app.mjs Ada Lovelace (Administrator) Known roles: admin, editor
formatDisplayName is imported without braces because it is the default export. roleLabels is imported inside braces because it is a named export.
$ rm app.mjs profile-format.mjs