A higher order component for loading components with promises
—
Core functionality for creating loadable components that dynamically load modules before rendering. This enables code splitting at the component level, reducing initial bundle sizes and improving application performance.
Creates a loadable component that handles dynamic imports with loading states, error handling, and customizable rendering.
/**
* Creates a higher-order component for dynamic component loading
* @param options - Configuration object for the loadable component
* @returns LoadableComponent class
*/
function Loadable(options: LoadableOptions): LoadableComponent;
interface LoadableOptions {
/** Function that returns a promise resolving to the module */
loader: () => Promise<any>;
/** Component to render during loading/error states */
loading: LoadingComponent;
/** Delay in milliseconds before showing loading component (default: 200) */
delay?: number;
/** Timeout in milliseconds before showing timeout state (default: null) */
timeout?: number;
/** Custom render function for the loaded module */
render?: (loaded: any, props: any) => React.ReactElement;
/** Function returning webpack module IDs for SSR */
webpack?: () => number[];
/** Array of module paths for SSR */
modules?: string[];
}Usage Examples:
import React from 'react';
import Loadable from 'react-loadable';
// Simple loadable component
const LoadableButton = Loadable({
loader: () => import('./Button'),
loading: () => <div>Loading button...</div>,
});
// Advanced configuration with error handling
const LoadableChart = Loadable({
loader: () => import('./Chart'),
loading: LoadingComponent,
delay: 300,
timeout: 10000,
render(loaded, props) {
const Chart = loaded.default;
return <Chart {...props} />;
},
});
function LoadingComponent(props) {
if (props.error) {
return <div>Error! <button onClick={props.retry}>Retry</button></div>;
} else if (props.timedOut) {
return <div>Taking a long time... <button onClick={props.retry}>Retry</button></div>;
} else if (props.pastDelay) {
return <div>Loading...</div>;
} else {
return null;
}
}The component returned by Loadable(), which renders the loading component while the module loads and then renders the loaded component.
/**
* Component created by Loadable function
*/
interface LoadableComponent extends React.Component {
/** Static method to preload the component */
static preload(): Promise<any>;
}Usage Examples:
// Preload component on user interaction
const LoadableModal = Loadable({
loader: () => import('./Modal'),
loading: Loading,
});
function App() {
const [showModal, setShowModal] = useState(false);
const handleMouseOver = () => {
// Preload component when user hovers
LoadableModal.preload();
};
const handleClick = () => {
setShowModal(true);
};
return (
<div>
<button onMouseOver={handleMouseOver} onClick={handleClick}>
Show Modal
</button>
{showModal && <LoadableModal onClose={() => setShowModal(false)} />}
</div>
);
}The loading component receives props that describe the current loading state and provide error recovery functionality.
/**
* Props passed to the loading component
*/
interface LoadingComponentProps {
/** Whether the module is currently loading */
isLoading: boolean;
/** Whether the delay threshold has been exceeded */
pastDelay: boolean;
/** Whether the timeout threshold has been exceeded */
timedOut: boolean;
/** Error object if loading failed, null otherwise */
error: Error | null;
/** Function to retry loading after an error */
retry: () => void;
}
/**
* Component type for loading states
*/
type LoadingComponent = React.ComponentType<LoadingComponentProps>;Usage Examples:
// Comprehensive loading component
function LoadingComponent(props) {
if (props.error) {
return (
<div className="error">
<p>Failed to load component</p>
<button onClick={props.retry}>Try Again</button>
</div>
);
}
if (props.timedOut) {
return (
<div className="timeout">
<p>Taking longer than expected...</p>
<button onClick={props.retry}>Retry</button>
</div>
);
}
if (props.pastDelay) {
return <div className="loading">Loading...</div>;
}
return null; // Don't show anything initially
}
// Minimal loading component
const SimpleLoading = (props) =>
props.error ? <div>Error!</div> :
props.pastDelay ? <div>Loading...</div> : null;Customize how the loaded module is rendered using the render option.
/**
* Custom render function type
* @param loaded - The resolved module from the loader
* @param props - Props passed to the LoadableComponent
* @returns React element to render
*/
type RenderFunction = (loaded: any, props: any) => React.ReactElement;Usage Examples:
// Render specific export from module
const LoadableIcon = Loadable({
loader: () => import('./icons'),
loading: Loading,
render(loaded, props) {
const Icon = loaded[props.iconName];
return <Icon {...props} />;
},
});
// Add props transformation
const LoadableAPI = Loadable({
loader: () => import('./APIComponent'),
loading: Loading,
render(loaded, props) {
const APIComponent = loaded.default;
return <APIComponent {...props} apiKey={process.env.REACT_APP_API_KEY} />;
},
});Control when the loading component appears to avoid flash of loading content for fast-loading components.
// Show loading immediately
const LoadableImmediate = Loadable({
loader: () => import('./Component'),
loading: Loading,
delay: 0,
});
// Wait 500ms before showing loading
const LoadableDelayed = Loadable({
loader: () => import('./Component'),
loading: Loading,
delay: 500,
});Set a timeout for loading operations to handle slow network conditions.
// 5 second timeout
const LoadableWithTimeout = Loadable({
loader: () => import('./Component'),
loading: Loading,
timeout: 5000,
});Configure webpack and modules options for server-side rendering support.
// Manual configuration (usually automated by Babel plugin)
const LoadableSSR = Loadable({
loader: () => import('./Component'),
loading: Loading,
webpack: () => [require.resolveWeak('./Component')],
modules: ['./Component'],
});Install with Tessl CLI
npx tessl i tessl/npm-react-loadable