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 exportThis mismatch causes the module system to look for a default export that doesn't exist.
Additional Problems
- Code duplication:
TableCsv.jscontains 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 classCleaning 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 sameKey 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.