Skip to content

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:

  1. Doesn't export anything
  2. Only has named exports
  3. 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:

js
class TableCsv { 
  // Class implementation
}
// No export statement!

Meanwhile, main.js attempts to import it as a default export:

js
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

  1. Code duplication: TableCsv.js contains logic for UI elements that should only exist in main.js
  2. 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:

js
export default TableCsv;

Option 2: Inline Default Export (Preferred)

Modify the class declaration:

js
export default class TableCsv {
  // Class implementation
}

Corresponding Import

Since we're using a default export, retain the original import style in main.js:

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:

js
export default class TableCsv {
  /**
   * @param {HTMLTableElement} root The table element
   */
  constructor(root) {
    this.root = root;
  }

  // ... (Keep only class methods, no UI logic)
}
js
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:

js
export class TableCsv {  // Use named export
  // Class implementation
}
js
import { TableCsv } from "./TableCsv.js"; // Use braces for named import

// Remainder of code remains the same

Key Differences: Default vs Named Exports

FeatureDefault ExportNamed Export
Syntaxexport default MyClassexport class MyClass
Importimport MyClass from './file.js'import { MyClass } from './file.js'
Modules Per FileOnly one default exportMultiple named exports
RenamingAlways possible without {}Requires as keyword

Complete Solution Code

View Final Code Implementation
js
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>
      `
    );
  }
}
js
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

  1. Module Compatibility: Ensure your HTML uses type="module":

    html
    <script type="module" src="main.js"></script>
  2. Server Requirement: Module imports require a server (local HTTP server, not file:// protocol)

  3. 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.