Module Export Error: Missing 'default' Export
Problem Statement
When working with JavaScript modules, you may encounter the error: SyntaxError: The requested module does not provide an export named 'default'
. This occurs when your code attempts to import a default export from a module that either:
- Doesn't export anything
- Only has named exports
- Isn't properly configured as an ES module
This error commonly happens when you try to import
a JavaScript module using the default import syntax (import MyModule from './my-module.js'
) but the module doesn't have a default
export defined.
Why This Error Occurs
Core Issue
In the provided code, TableCsv.js
defines a class but does not export it, making the class inaccessible to other files:
class TableCsv {
// Class implementation
}
// No export statement!
Meanwhile, main.js
attempts to import it as a default export:
import TableCsv from "./TableCsv.js"; // Requires default export
This mismatch causes the module system to look for a default export that doesn't exist.
Additional Problems
- Code duplication:
TableCsv.js
contains logic for UI elements that should only exist inmain.js
- Improper import syntax: Using named-import syntax (
{ TableCsv }
) when default export doesn't exist
WARNING
Attempting to create multiple default exports in a module will generate TypeScript error TS2528
: "A module cannot have multiple default exports."
Solution: Adding Proper Export
To resolve this error, properly export your class:
Option 1: Explicit Default Export
Add at the bottom of TableCsv.js
:
export default TableCsv;
Option 2: Inline Default Export (Preferred)
Modify the class declaration:
export default class TableCsv {
// Class implementation
}
Corresponding Import
Since we're using a default export, retain the original import style in main.js
:
import TableCsv from "./TableCsv.js"; // Access exported class
Cleaning Up Your Code
The original TableCsv.js
contains duplicated UI logic that belongs in main.js
. Clean it up:
export default class TableCsv {
/**
* @param {HTMLTableElement} root The table element
*/
constructor(root) {
this.root = root;
}
// ... (Keep only class methods, no UI logic)
}
import TableCsv from "./TableCsv.js";
const tableRoot = document.querySelector("#csvRoot");
const csvFileInput = document.querySelector("#csvFileInput");
const tableCsv = new TableCsv(tableRoot);
csvFileInput.addEventListener("change", e => {
Papa.parse(csvFileInput.files[0], {
delimiter: ",",
skipEmptyLines: true,
complete: results => {
tableCsv.update(results.data.slice(1), results.data[0]);
}
});
});
Named Exports Alternative
If you prefer named exports:
export class TableCsv { // Use named export
// Class implementation
}
import { TableCsv } from "./TableCsv.js"; // Use braces for named import
// Remainder of code remains the same
Key Differences: Default vs Named Exports
Feature | Default Export | Named Export |
---|---|---|
Syntax | export default MyClass | export class MyClass |
Import | import MyClass from './file.js' | import { MyClass } from './file.js' |
Modules Per File | Only one default export | Multiple named exports |
Renaming | Always possible without {} | Requires as keyword |
Complete Solution Code
View Final Code Implementation
export default class TableCsv {
constructor(root) {
this.root = root;
}
update(data, headerColumns = []) {
this.clear();
this.setHeader(headerColumns);
this.setBody(data);
}
clear() {
this.root.innerHTML = "";
}
setHeader(headerColumns) {
this.root.insertAdjacentHTML(
"afterbegin",
`
<thead>
<tr>
${headerColumns.map(text => `<th>${text}</th>`).join("")}
</tr>
</thead>
`
);
}
setBody(data) {
const rowsHtml = data.map(row => `
<tr>
${row.map(text => `<td>${text}</td>`).join("")}
</tr>
`);
this.root.insertAdjacentHTML(
"beforeend",
`
<tbody>
${rowsHtml.join("")}
</tbody>
`
);
}
}
import TableCsv from "./TableCsv.js";
const tableRoot = document.querySelector("#csvRoot");
const csvFileInput = document.querySelector("#csvFileInput");
const tableCsv = new TableCsv(tableRoot);
csvFileInput.addEventListener("change", e => {
Papa.parse(csvFileInput.files[0], {
delimiter: ",",
skipEmptyLines: true,
complete: results => {
tableCsv.update(results.data.slice(1), results.data[0]);
}
});
});
Important Notes
Module Compatibility: Ensure your HTML uses
type="module"
:html<script type="module" src="main.js"></script>
Server Requirement: Module imports require a server (local HTTP server, not
file://
protocol)Library Compatibility: Verify Papa Parse has been correctly loaded
This solution maintains clean separation between class implementation and UI setup while following modern JavaScript module standards. Remember that proper export/import syntax is crucial for maintaining modular, maintainable JavaScript applications.