CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-loadable

A higher order component for loading components with promises

Pending
Overview
Eval results
Files

build-integration.mddocs/

Build Tool Integration

React Loadable provides build tool plugins to automate server-side rendering setup and optimize the development experience. This includes a Babel plugin for automatic module resolution and a Webpack plugin for generating bundle manifests.

Capabilities

Babel Plugin

Automatically adds webpack and modules options to Loadable components, eliminating the need for manual configuration in SSR setups.

/**
 * Babel plugin that transforms Loadable calls to include webpack and modules options
 * Plugin name: "react-loadable/babel"
 */

Configuration:

{
  "plugins": ["react-loadable/babel"]
}

Transform Example:

// Input
import Loadable from 'react-loadable';

const LoadableComponent = Loadable({
  loader: () => import('./MyComponent'),
  loading: Loading,
});

const LoadableMap = Loadable.Map({
  loader: {
    One: () => import('./One'),
    Two: () => import('./Two'),
  },
  loading: Loading,
  render: (loaded, props) => <div>{/* ... */}</div>,
});
// Output (automatically generated)
import Loadable from 'react-loadable';
import path from 'path';

const LoadableComponent = Loadable({
  loader: () => import('./MyComponent'),
  loading: Loading,
  webpack: () => [require.resolveWeak('./MyComponent')],
  modules: [path.join(__dirname, './MyComponent')],
});

const LoadableMap = Loadable.Map({
  loader: {
    One: () => import('./One'),
    Two: () => import('./Two'),
  },
  loading: Loading,
  render: (loaded, props) => <div>{/* ... */}</div>,
  webpack: () => [require.resolveWeak('./One'), require.resolveWeak('./Two')],
  modules: [path.join(__dirname, './One'), path.join(__dirname, './Two')],
});

Webpack Plugin

Generates a manifest file mapping modules to webpack bundles, enabling server-side rendering to determine which bundles to include for rendered components.

/**
 * Webpack plugin for generating loadable component manifest
 */
class ReactLoadablePlugin {
  /**
   * @param options - Plugin configuration
   * @param options.filename - Path where manifest JSON should be written
   */
  constructor(options: { filename: string });
}

Configuration:

// webpack.config.js
const { ReactLoadablePlugin } = require('react-loadable/webpack');

module.exports = {
  plugins: [
    new ReactLoadablePlugin({
      filename: './dist/react-loadable.json',
    }),
  ],
};

Generated Manifest Structure:

{
  "./src/components/Header": [
    {
      "id": 0,
      "name": "./src/components/Header",
      "file": "0.js",
      "publicPath": "/dist/0.js"
    }
  ],
  "./src/components/Footer": [
    {
      "id": 1, 
      "name": "./src/components/Footer",
      "file": "1.js",
      "publicPath": "/dist/1.js"
    }
  ]
}

Bundle Resolution

Convert module names to bundle information for including the correct script tags in server-rendered HTML.

/**
 * Converts module IDs to bundle information using webpack manifest
 * @param stats - Webpack stats object or manifest from ReactLoadablePlugin
 * @param modules - Array of module names reported by Loadable.Capture
 * @returns Array of bundle objects containing file paths and metadata
 */
function getBundles(stats: WebpackStats, modules: string[]): Bundle[];

interface Bundle {
  /** Webpack module ID */
  id: number | string;
  /** Module name or path */
  name: string;
  /** Bundle filename */
  file: string;
  /** Full public path to bundle */
  publicPath: string;
}

interface WebpackStats {
  [moduleName: string]: Bundle[];
}

Usage Examples:

import { getBundles } from 'react-loadable/webpack';
import stats from './dist/react-loadable.json';

// Server-side rendering
app.get('*', (req, res) => {
  const modules = [];
  
  const html = ReactDOMServer.renderToString(
    <Loadable.Capture report={moduleName => modules.push(moduleName)}>
      <App />
    </Loadable.Capture>
  );
  
  // Convert modules to bundles
  const bundles = getBundles(stats, modules);
  
  // Generate script tags
  const scripts = bundles.map(bundle => 
    `<script src="${bundle.publicPath}"></script>`
  ).join('\n');
  
  res.send(`
    <!DOCTYPE html>
    <html>
      <body>
        <div id="app">${html}</div>
        ${scripts}
        <script src="/main.js"></script>
      </body>
    </html>
  `);
});

Complete Build Setup

Webpack Configuration

// webpack.config.js
const path = require('path');
const { ReactLoadablePlugin } = require('react-loadable/webpack');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[chunkhash].js',
    publicPath: '/dist/',
  },
  plugins: [
    // Generate manifest for server-side rendering
    new ReactLoadablePlugin({
      filename: path.resolve(__dirname, 'dist/react-loadable.json'),
    }),
    
    // Extract webpack manifest (required for proper chunk loading)
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity,
    }),
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
    },
  },
};

Babel Configuration

{
  "presets": ["@babel/preset-react", "@babel/preset-env"],
  "plugins": [
    "react-loadable/babel",
    "@babel/plugin-syntax-dynamic-import"
  ]
}

Package.json Scripts

{
  "scripts": {
    "build": "webpack --mode=production",
    "build:dev": "webpack --mode=development",
    "start": "node server.js",
    "dev": "webpack-dev-server --mode=development"
  }
}

Advanced Build Configurations

Custom Webpack Public Path

Handle dynamic public paths for CDN deployment:

// webpack.config.js
module.exports = {
  output: {
    publicPath: process.env.CDN_URL || '/dist/',
  },
  plugins: [
    new ReactLoadablePlugin({
      filename: './dist/react-loadable.json',
    }),
  ],
};

// Server usage
const bundles = getBundles(stats, modules);
const scripts = bundles.map(bundle => {
  // Use bundle.publicPath which includes the configured publicPath
  return `<script src="${bundle.publicPath}"></script>`;
}).join('\n');

Development vs Production

// webpack.config.js
const isProduction = process.env.NODE_ENV === 'production';

module.exports = {
  plugins: [
    new ReactLoadablePlugin({
      filename: isProduction 
        ? './dist/react-loadable.json'
        : './dev/react-loadable.json',
    }),
  ].filter(Boolean),
  
  optimization: isProduction ? {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
      },
    },
  } : {},
};

Multiple Entry Points

Handle applications with multiple entry points:

// webpack.config.js
module.exports = {
  entry: {
    main: './src/main.js',
    admin: './src/admin.js',
  },
  plugins: [
    new ReactLoadablePlugin({
      filename: './dist/react-loadable.json',
    }),
  ],
};

// Server usage - filter bundles by entry point
function getBundlesForEntry(stats, modules, entryName) {
  const bundles = getBundles(stats, modules);
  return bundles.filter(bundle => 
    bundle.file.includes(entryName) || 
    !bundle.file.match(/^(main|admin)\./)
  );
}

TypeScript Integration

// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "moduleResolution": "node",
    "jsx": "react",
    "allowSyntheticDefaultImports": true
  }
}
// webpack.config.js
module.exports = {
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
    ],
  },
};

Build Optimization

Bundle Analysis

Analyze which components are being code-split:

// Build script to analyze loadable components
const fs = require('fs');
const stats = require('./dist/react-loadable.json');

console.log('Loadable components:');
Object.keys(stats).forEach(module => {
  const bundles = stats[module];
  console.log(`${module}:`);
  bundles.forEach(bundle => {
    console.log(`  - ${bundle.file} (${bundle.id})`);
  });
});

Bundle Size Optimization

// webpack.config.js - Optimize chunk sizes
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 20000,
      maxSize: 200000,
      cacheGroups: {
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
      },
    },
  },
};

Preload Optimization

Generate preload hints for critical bundles:

// Server-side bundle preloading
function generatePreloadLinks(bundles) {
  return bundles
    .filter(bundle => bundle.file.endsWith('.js'))
    .map(bundle => 
      `<link rel="preload" href="${bundle.publicPath}" as="script">`
    )
    .join('\n');
}

// Usage in server
const bundles = getBundles(stats, modules);
const preloadLinks = generatePreloadLinks(bundles);

res.send(`
  <!DOCTYPE html>
  <html>
    <head>
      ${preloadLinks}
    </head>
    <body>
      <!-- ... -->
    </body>
  </html>
`);

Install with Tessl CLI

npx tessl i tessl/npm-react-loadable

docs

build-integration.md

dynamic-loading.md

index.md

multi-resource-loading.md

server-side-rendering.md

tile.json