A higher order component for loading components with promises
—
Advanced functionality for loading multiple resources in parallel using Loadable.Map. This enables complex loading scenarios where components depend on multiple modules, data sources, or external resources.
Creates a loadable component that loads multiple resources in parallel and provides them to a custom render function.
/**
* Creates a loadable component that loads multiple resources in parallel
* @param options - Configuration object with loader map and render function
* @returns LoadableComponent class
*/
function LoadableMap(options: LoadableMapOptions): LoadableComponent;
interface LoadableMapOptions {
/** Object mapping keys to loader functions */
loader: { [key: string]: () => Promise<any> };
/** Component to render during loading/error states */
loading: LoadingComponent;
/** Required render function to combine loaded resources */
render: (loaded: { [key: string]: any }, props: any) => React.ReactElement;
/** Delay in milliseconds before showing loading component (default: 200) */
delay?: number;
/** Timeout in milliseconds before showing timeout state (default: null) */
timeout?: number;
/** 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';
// Load component and data together
const LoadableUserProfile = Loadable.Map({
loader: {
Component: () => import('./UserProfile'),
translations: () => fetch('/api/i18n/user-profile.json').then(res => res.json()),
theme: () => import('./themes/default'),
},
loading: LoadingSpinner,
render(loaded, props) {
const UserProfile = loaded.Component.default;
return (
<UserProfile
{...props}
translations={loaded.translations}
theme={loaded.theme.default}
/>
);
},
});
// Load multiple related components
const LoadableDashboard = Loadable.Map({
loader: {
Header: () => import('./DashboardHeader'),
Sidebar: () => import('./DashboardSidebar'),
Content: () => import('./DashboardContent'),
},
loading: LoadingDashboard,
render(loaded, props) {
const Header = loaded.Header.default;
const Sidebar = loaded.Sidebar.default;
const Content = loaded.Content.default;
return (
<div className="dashboard">
<Header user={props.user} />
<div className="dashboard-body">
<Sidebar />
<Content data={props.data} />
</div>
</div>
);
},
});The render function is required for Loadable.Map and receives all loaded resources as a single object.
/**
* Render function for combining multiple loaded resources
* @param loaded - Object containing all loaded resources by key
* @param props - Props passed to the LoadableComponent
* @returns React element combining the loaded resources
*/
type LoadableMapRenderFunction = (
loaded: { [key: string]: any },
props: any
) => React.ReactElement;Usage Examples:
// Complex data transformation
const LoadableChart = Loadable.Map({
loader: {
ChartComponent: () => import('./Chart'),
data: () => fetch('/api/chart-data').then(res => res.json()),
config: () => import('./chart-config.json'),
},
loading: Loading,
render(loaded, props) {
const Chart = loaded.ChartComponent.default;
const processedData = processChartData(loaded.data, loaded.config);
return <Chart data={processedData} {...props} />;
},
});
// Conditional rendering based on loaded resources
const LoadableConditional = Loadable.Map({
loader: {
AdminPanel: () => import('./AdminPanel'),
UserPanel: () => import('./UserPanel'),
permissions: () => fetch('/api/user/permissions').then(res => res.json()),
},
loading: Loading,
render(loaded, props) {
const isAdmin = loaded.permissions.includes('admin');
const Panel = isAdmin ? loaded.AdminPanel.default : loaded.UserPanel.default;
return <Panel {...props} permissions={loaded.permissions} />;
},
});Multi-resource loading handles complex scenarios where some resources may load faster than others or where individual resources may fail.
// Loading component for multi-resource scenarios
function MultiResourceLoading(props) {
if (props.error) {
return (
<div className="multi-load-error">
<h3>Failed to load resources</h3>
<p>One or more required resources failed to load</p>
<button onClick={props.retry}>Retry All</button>
</div>
);
}
if (props.timedOut) {
return (
<div className="multi-load-timeout">
<h3>Loading is taking longer than expected</h3>
<button onClick={props.retry}>Retry</button>
</div>
);
}
if (props.pastDelay) {
return (
<div className="multi-load-loading">
<div>Loading dashboard components...</div>
<div className="loading-spinner" />
</div>
);
}
return null;
}Handle scenarios where some resources depend on others by structuring the loader appropriately.
const LoadableWithDependencies = Loadable.Map({
loader: {
config: () => fetch('/api/config').then(res => res.json()),
// Note: All loaders start simultaneously, but you can handle dependencies in render
component: () => import('./ConfigurableComponent'),
},
loading: Loading,
render(loaded, props) {
const Component = loaded.component.default;
// Apply configuration to component
return <Component config={loaded.config} {...props} />;
},
});Combine static and dynamic resource loading based on props.
function createDynamicLoader(locale) {
return Loadable.Map({
loader: {
Component: () => import('./LocalizedComponent'),
translations: () => import(`./i18n/${locale}.json`),
dateUtils: () => import(`./utils/date-${locale}.js`),
},
loading: Loading,
render(loaded, props) {
const Component = loaded.Component.default;
return (
<Component
{...props}
translations={loaded.translations}
formatDate={loaded.dateUtils.formatDate}
/>
);
},
});
}
// Usage
const LoadableEnglish = createDynamicLoader('en');
const LoadableFrench = createDynamicLoader('fr');Load different types of resources including modules, JSON data, external APIs, and assets.
const LoadableRichContent = Loadable.Map({
loader: {
// React component
Editor: () => import('./RichTextEditor'),
// JSON configuration
editorConfig: () => import('./editor-config.json'),
// External API data
userData: () => fetch('/api/user/preferences').then(res => res.json()),
// Dynamic import based on feature flags
plugins: () =>
fetch('/api/feature-flags')
.then(res => res.json())
.then(flags => flags.advancedEditor ?
import('./editor-plugins/advanced') :
import('./editor-plugins/basic')
),
},
loading: RichContentLoading,
render(loaded, props) {
const Editor = loaded.Editor.default;
return (
<Editor
{...props}
config={loaded.editorConfig}
userPreferences={loaded.userData}
plugins={loaded.plugins.default}
/>
);
},
});Install with Tessl CLI
npx tessl i tessl/npm-react-loadable