How to fix React.lazy promise did not resolve to a default component in React

ReactINTERMEDIATEMEDIUMUpdated Dec 10, 2025· 153 views

This error occurs when React.lazy() imports a module that lacks a default export. React.lazy() only supports default exports; if your module uses named exports, you must create a wrapper to re-export them as default, or use promise chaining to transform named exports into the default export structure.

Quick fix

Fixes React.lazy promise did not resolve to a default component

// MyComponent.js
export default function MyComponent() {
  return <div>Hello</div>;
}
  1. Verify the imported module has a default export
  2. Fix React.lazy with named exports using an intermediate module
  3. Use promise chaining with .then() to wrap named exports
  4. Ensure the promise resolves with a valid React component
react — error
// MyComponent.js
export default function MyComponent() {
return <div>Hello</div>;
}
React.lazy promise did not resolve to a default component

What this error means

The "React.lazy promise did not resolve to a default component" error indicates that the dynamic import in your React.lazy() function resolved to a module that doesn't have a default export, or the resolved value isn't a valid React component. React.lazy() is designed to work with dynamic imports that resolve to modules with a default export. When you call lazy(() => import('./Component')), React expects the promise to resolve with a module object that has a .default property containing a React component (either a function component or class component). The error occurs in two scenarios: 1. The imported module uses named exports instead of a default export (e.g., export function MyComponent {}) 2. The promise resolves to undefined or an invalid component type This is an intentional design limitation—React.lazy() doesn't automatically unwrap named exports. It strictly expects modules with default exports to ensure proper tree-shaking and bundling behavior.

Typical symptoms

  • React throws "Element type is invalid. Received a promise that resolves to: undefined"
  • Dynamic import with lazy() renders an error or blank page instead of the component
  • Console shows "Lazy element type must resolve to a class or function component"
  • Suspense fallback never resolves and the lazy component never appears
  • Error occurs when component should load in a Suspense boundary
  • Works with static imports but fails with React.lazy() and dynamic imports

Common causes

  1. Imported module uses named exports (export function MyComponent) instead of default export
  2. Promise from dynamic import resolves to undefined instead of a module with a component
  3. Using async/await in the lazy function without properly returning the resolved module
  4. Chained .then() promise that doesn't return the expected module structure
  5. Exporting a non-component value as default (object, string, null)
  6. Typo or incorrect module path that fails to load the component file
  7. Webpack or build tool not properly resolving the dynamic import path

How to fix "React.lazy promise did not resolve to a default component"

1Verify the imported module has a default export

First, check that the component file you're importing exports a React component as the default export.

Correct:

javascript
// MyComponent.js
export default function MyComponent() {
  return <div>Hello</div>;
}

Or with named export converted to default:

javascript
// MyComponent.js
export function MyComponent() {
  return <div>Hello</div>;
}

Incorrect (will cause error):

javascript
// MyComponent.js
export function MyComponent() {
  return <div>Hello</div>;
}
// No default export!

Ensure the component file explicitly exports something as export default at the module level.

2Fix React.lazy with named exports using an intermediate module

If the module uses named exports but you can't change it, create a wrapper module that re-exports it as default.

Create MyComponentWrapper.js:

javascript
// MyComponentWrapper.js
export { MyComponent as default } from './MyComponent';

Then use lazy with the wrapper:

javascript
import { lazy, Suspense } from 'react';

const MyComponent = lazy(() => import('./MyComponentWrapper'));

export default function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <MyComponent />
    </Suspense>
  );
}

This approach maintains proper tree-shaking and bundling while allowing lazy loading of named exports.

3Use promise chaining with .then() to wrap named exports

Alternatively, transform named exports into the expected module structure using promise chaining:

javascript
import { lazy, Suspense } from 'react';

const MyComponent = lazy(() =>
  import('./MyComponent').then(module => ({
    default: module.MyComponent
  }))
);

export default function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <MyComponent />
    </Suspense>
  );
}

The .then() callback receives the imported module and returns an object with a default property containing the component. React.lazy() then receives the expected structure.

For multiple named exports:

javascript
const ComponentA = lazy(() =>
  import('./Components').then(m => ({ default: m.ComponentA }))
);

const ComponentB = lazy(() =>
  import('./Components').then(m => ({ default: m.ComponentB }))
);
4Ensure the promise resolves with a valid React component

The dynamic import must resolve to a module where the default property is a React component (function or class).

Valid:

javascript
// Button.js
export default function Button() {
  return <button>Click me</button>;
}

// Usage
const Button = lazy(() => import('./Button'));

Valid (class component):

javascript
// FormComponent.js
export default class FormComponent extends React.Component {
  render() {
    return <form>...</form>;
  }
}

// Usage
const FormComponent = lazy(() => import('./FormComponent'));

Invalid:

javascript
// BadComponent.js
export default {
  name: 'MyComponent',
  render: () => <div>Hello</div>
};

// This will fail—the default export must be a React component, not an object
const BadComponent = lazy(() => import('./BadComponent'));

Verify that the default export is a callable function or class, not a plain object or other value.

5Check the import path and build configuration

Verify that the path passed to import() is correct and your build tool (webpack, Vite, etc.) properly resolves dynamic imports.

Correct:

javascript
const MyComponent = lazy(() => import('./components/MyComponent'));
const Other = lazy(() => import('@/components/Dialog'));  // With path aliases

Incorrect:

javascript
const MyComponent = lazy(() => import('./NonExistentFile'));  // Path doesn't exist

For path aliases (like @/), ensure your build config supports them:

javascript
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
});

Or in webpack:

javascript
// webpack.config.js
module.exports = {
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src/'),
    },
  },
};

Check the browser's Network tab to confirm the chunk is loading, or use your dev tools to verify the import resolution.

6Wrap lazy components in Suspense with a fallback

Always wrap lazy components in a Suspense boundary with a fallback UI. This prevents errors if the chunk fails to load or resolution is delayed:

javascript
import { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

export default function App() {
  return (
    <Suspense fallback={<div className="spinner">Loading component...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

The fallback UI is shown while the chunk loads. If the promise rejects, the nearest Error Boundary catches it:

javascript
import { lazy, Suspense, Component } from 'react';

const LazyComponent = lazy(() => import('./LazyComponent'));

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <div>Failed to load component. Please refresh the page.</div>;
    }
    return this.props.children;
  }
}

export default function App() {
  return (
    <ErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <LazyComponent />
      </Suspense>
    </ErrorBoundary>
  );
}
7Debug by logging the imported module

If you're still seeing the error, log the imported module to verify its structure:

javascript
const MyComponent = lazy(() =>
  import('./MyComponent').then(module => {
    console.log('Imported module:', module);
    console.log('Default export:', module.default);
    if (!module.default) {
      console.error('No default export found. Available exports:', Object.keys(module));
    }
    return module;
  })
);

This logs the module object and all available exports. Check the console to see:
- If module.default exists and is a function/class
- What other exports are available if default is missing
- Whether the module is being imported at all

Then adjust your lazy() call based on what you find:

javascript
// If you see named exports like MyComponent, use:
const MyComponent = lazy(() =>
  import('./MyComponent').then(m => ({ default: m.MyComponent }))
);

// If it's undefined, check the file path or export statement

Advanced notes

Tree-Shaking and Bundling: The reason React.lazy() requires default exports is to preserve tree-shaking. When your bundler (webpack, Vite) encounters a dynamic import with a default export, it can more reliably create a separate chunk for just that module. Named exports complicate this analysis, which is why the wrapper pattern is recommended over complex promise chains—it keeps the module boundary clean and explicit.

Next.js Dynamic Imports: If you're using Next.js, prefer next/dynamic over React.lazy() for server-side considerations:

typescript
import dynamic from 'next/dynamic';

const MyComponent = dynamic(() => import('./MyComponent'), {
  loading: () => <div>Loading...</div>,
  ssr: false, // Disable server-side rendering if needed
});

export default function Page() {
  return <MyComponent />;
}

Error Boundary Placement: Lazy component failures (network errors, invalid modules) should be caught by an Error Boundary, not Suspense. Suspense handles loading states; Error Boundary handles errors.

Bundle Analysis: Use tools like webpack-bundle-analyzer to verify that lazy imports create separate chunks:

bash
npm install --save-dev webpack-bundle-analyzer

This helps confirm that your dynamic imports are being split into separate files as intended, which is the main benefit of code splitting with lazy loading.

Module Federation: In Micro Frontend architectures, lazy components across module boundaries require additional configuration to handle remote entry points. Ensure the remote module exports a default component and that your host app's webpack/Vite config properly handles federated imports.

Frequently asked questions

What does "React.lazy promise did not resolve to a default component" mean?
lazy() function resolved to a module that doesn't have a default export, or the resolved value isn't a valid React component. lazy() is designed to work with dynamic imports that resolve to modules with a default export. default property containing a React component (either a function component or class component).
How do I fix "React.lazy promise did not resolve to a default component"?
This error occurs when React.lazy() imports a module that lacks a default export. React.lazy() only supports default exports; if your module uses named exports, you must create a wrapper to re-export them as default, or use promise chaining to transform named exports into the default export structure.
What causes "React.lazy promise did not resolve to a default component"?
Imported module uses named exports (export function MyComponent) instead of default export Promise from dynamic import resolves to undefined instead of a module with a component Using async/await in the lazy function without properly returning the resolved module
Is the "React.lazy promise did not resolve to a default component" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading