0
# Build Tool Integration
1
2
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.
3
4
## Capabilities
5
6
### Babel Plugin
7
8
Automatically adds `webpack` and `modules` options to Loadable components, eliminating the need for manual configuration in SSR setups.
9
10
```javascript { .api }
11
/**
12
* Babel plugin that transforms Loadable calls to include webpack and modules options
13
* Plugin name: "react-loadable/babel"
14
*/
15
```
16
17
**Configuration:**
18
19
```json
20
{
21
"plugins": ["react-loadable/babel"]
22
}
23
```
24
25
**Transform Example:**
26
27
```javascript
28
// Input
29
import Loadable from 'react-loadable';
30
31
const LoadableComponent = Loadable({
32
loader: () => import('./MyComponent'),
33
loading: Loading,
34
});
35
36
const LoadableMap = Loadable.Map({
37
loader: {
38
One: () => import('./One'),
39
Two: () => import('./Two'),
40
},
41
loading: Loading,
42
render: (loaded, props) => <div>{/* ... */}</div>,
43
});
44
```
45
46
```javascript
47
// Output (automatically generated)
48
import Loadable from 'react-loadable';
49
import path from 'path';
50
51
const LoadableComponent = Loadable({
52
loader: () => import('./MyComponent'),
53
loading: Loading,
54
webpack: () => [require.resolveWeak('./MyComponent')],
55
modules: [path.join(__dirname, './MyComponent')],
56
});
57
58
const LoadableMap = Loadable.Map({
59
loader: {
60
One: () => import('./One'),
61
Two: () => import('./Two'),
62
},
63
loading: Loading,
64
render: (loaded, props) => <div>{/* ... */}</div>,
65
webpack: () => [require.resolveWeak('./One'), require.resolveWeak('./Two')],
66
modules: [path.join(__dirname, './One'), path.join(__dirname, './Two')],
67
});
68
```
69
70
### Webpack Plugin
71
72
Generates a manifest file mapping modules to webpack bundles, enabling server-side rendering to determine which bundles to include for rendered components.
73
74
```javascript { .api }
75
/**
76
* Webpack plugin for generating loadable component manifest
77
*/
78
class ReactLoadablePlugin {
79
/**
80
* @param options - Plugin configuration
81
* @param options.filename - Path where manifest JSON should be written
82
*/
83
constructor(options: { filename: string });
84
}
85
```
86
87
**Configuration:**
88
89
```javascript
90
// webpack.config.js
91
const { ReactLoadablePlugin } = require('react-loadable/webpack');
92
93
module.exports = {
94
plugins: [
95
new ReactLoadablePlugin({
96
filename: './dist/react-loadable.json',
97
}),
98
],
99
};
100
```
101
102
**Generated Manifest Structure:**
103
104
```json
105
{
106
"./src/components/Header": [
107
{
108
"id": 0,
109
"name": "./src/components/Header",
110
"file": "0.js",
111
"publicPath": "/dist/0.js"
112
}
113
],
114
"./src/components/Footer": [
115
{
116
"id": 1,
117
"name": "./src/components/Footer",
118
"file": "1.js",
119
"publicPath": "/dist/1.js"
120
}
121
]
122
}
123
```
124
125
### Bundle Resolution
126
127
Convert module names to bundle information for including the correct script tags in server-rendered HTML.
128
129
```javascript { .api }
130
/**
131
* Converts module IDs to bundle information using webpack manifest
132
* @param stats - Webpack stats object or manifest from ReactLoadablePlugin
133
* @param modules - Array of module names reported by Loadable.Capture
134
* @returns Array of bundle objects containing file paths and metadata
135
*/
136
function getBundles(stats: WebpackStats, modules: string[]): Bundle[];
137
138
interface Bundle {
139
/** Webpack module ID */
140
id: number | string;
141
/** Module name or path */
142
name: string;
143
/** Bundle filename */
144
file: string;
145
/** Full public path to bundle */
146
publicPath: string;
147
}
148
149
interface WebpackStats {
150
[moduleName: string]: Bundle[];
151
}
152
```
153
154
**Usage Examples:**
155
156
```javascript
157
import { getBundles } from 'react-loadable/webpack';
158
import stats from './dist/react-loadable.json';
159
160
// Server-side rendering
161
app.get('*', (req, res) => {
162
const modules = [];
163
164
const html = ReactDOMServer.renderToString(
165
<Loadable.Capture report={moduleName => modules.push(moduleName)}>
166
<App />
167
</Loadable.Capture>
168
);
169
170
// Convert modules to bundles
171
const bundles = getBundles(stats, modules);
172
173
// Generate script tags
174
const scripts = bundles.map(bundle =>
175
`<script src="${bundle.publicPath}"></script>`
176
).join('\n');
177
178
res.send(`
179
<!DOCTYPE html>
180
<html>
181
<body>
182
<div id="app">${html}</div>
183
${scripts}
184
<script src="/main.js"></script>
185
</body>
186
</html>
187
`);
188
});
189
```
190
191
## Complete Build Setup
192
193
### Webpack Configuration
194
195
```javascript
196
// webpack.config.js
197
const path = require('path');
198
const { ReactLoadablePlugin } = require('react-loadable/webpack');
199
200
module.exports = {
201
entry: './src/index.js',
202
output: {
203
path: path.resolve(__dirname, 'dist'),
204
filename: '[name].[chunkhash].js',
205
publicPath: '/dist/',
206
},
207
plugins: [
208
// Generate manifest for server-side rendering
209
new ReactLoadablePlugin({
210
filename: path.resolve(__dirname, 'dist/react-loadable.json'),
211
}),
212
213
// Extract webpack manifest (required for proper chunk loading)
214
new webpack.optimize.CommonsChunkPlugin({
215
name: 'manifest',
216
minChunks: Infinity,
217
}),
218
],
219
optimization: {
220
splitChunks: {
221
chunks: 'all',
222
},
223
},
224
};
225
```
226
227
### Babel Configuration
228
229
```json
230
{
231
"presets": ["@babel/preset-react", "@babel/preset-env"],
232
"plugins": [
233
"react-loadable/babel",
234
"@babel/plugin-syntax-dynamic-import"
235
]
236
}
237
```
238
239
### Package.json Scripts
240
241
```json
242
{
243
"scripts": {
244
"build": "webpack --mode=production",
245
"build:dev": "webpack --mode=development",
246
"start": "node server.js",
247
"dev": "webpack-dev-server --mode=development"
248
}
249
}
250
```
251
252
## Advanced Build Configurations
253
254
### Custom Webpack Public Path
255
256
Handle dynamic public paths for CDN deployment:
257
258
```javascript
259
// webpack.config.js
260
module.exports = {
261
output: {
262
publicPath: process.env.CDN_URL || '/dist/',
263
},
264
plugins: [
265
new ReactLoadablePlugin({
266
filename: './dist/react-loadable.json',
267
}),
268
],
269
};
270
271
// Server usage
272
const bundles = getBundles(stats, modules);
273
const scripts = bundles.map(bundle => {
274
// Use bundle.publicPath which includes the configured publicPath
275
return `<script src="${bundle.publicPath}"></script>`;
276
}).join('\n');
277
```
278
279
### Development vs Production
280
281
```javascript
282
// webpack.config.js
283
const isProduction = process.env.NODE_ENV === 'production';
284
285
module.exports = {
286
plugins: [
287
new ReactLoadablePlugin({
288
filename: isProduction
289
? './dist/react-loadable.json'
290
: './dev/react-loadable.json',
291
}),
292
].filter(Boolean),
293
294
optimization: isProduction ? {
295
splitChunks: {
296
chunks: 'all',
297
cacheGroups: {
298
vendor: {
299
test: /[\\/]node_modules[\\/]/,
300
name: 'vendors',
301
chunks: 'all',
302
},
303
},
304
},
305
} : {},
306
};
307
```
308
309
### Multiple Entry Points
310
311
Handle applications with multiple entry points:
312
313
```javascript
314
// webpack.config.js
315
module.exports = {
316
entry: {
317
main: './src/main.js',
318
admin: './src/admin.js',
319
},
320
plugins: [
321
new ReactLoadablePlugin({
322
filename: './dist/react-loadable.json',
323
}),
324
],
325
};
326
327
// Server usage - filter bundles by entry point
328
function getBundlesForEntry(stats, modules, entryName) {
329
const bundles = getBundles(stats, modules);
330
return bundles.filter(bundle =>
331
bundle.file.includes(entryName) ||
332
!bundle.file.match(/^(main|admin)\./)
333
);
334
}
335
```
336
337
### TypeScript Integration
338
339
```json
340
// tsconfig.json
341
{
342
"compilerOptions": {
343
"target": "es5",
344
"module": "esnext",
345
"moduleResolution": "node",
346
"jsx": "react",
347
"allowSyntheticDefaultImports": true
348
}
349
}
350
```
351
352
```javascript
353
// webpack.config.js
354
module.exports = {
355
resolve: {
356
extensions: ['.js', '.jsx', '.ts', '.tsx'],
357
},
358
module: {
359
rules: [
360
{
361
test: /\.(js|jsx|ts|tsx)$/,
362
exclude: /node_modules/,
363
use: {
364
loader: 'babel-loader',
365
},
366
},
367
],
368
},
369
};
370
```
371
372
## Build Optimization
373
374
### Bundle Analysis
375
376
Analyze which components are being code-split:
377
378
```javascript
379
// Build script to analyze loadable components
380
const fs = require('fs');
381
const stats = require('./dist/react-loadable.json');
382
383
console.log('Loadable components:');
384
Object.keys(stats).forEach(module => {
385
const bundles = stats[module];
386
console.log(`${module}:`);
387
bundles.forEach(bundle => {
388
console.log(` - ${bundle.file} (${bundle.id})`);
389
});
390
});
391
```
392
393
### Bundle Size Optimization
394
395
```javascript
396
// webpack.config.js - Optimize chunk sizes
397
module.exports = {
398
optimization: {
399
splitChunks: {
400
chunks: 'all',
401
minSize: 20000,
402
maxSize: 200000,
403
cacheGroups: {
404
default: {
405
minChunks: 2,
406
priority: -20,
407
reuseExistingChunk: true,
408
},
409
vendor: {
410
test: /[\\/]node_modules[\\/]/,
411
priority: -10,
412
reuseExistingChunk: true,
413
},
414
},
415
},
416
},
417
};
418
```
419
420
### Preload Optimization
421
422
Generate preload hints for critical bundles:
423
424
```javascript
425
// Server-side bundle preloading
426
function generatePreloadLinks(bundles) {
427
return bundles
428
.filter(bundle => bundle.file.endsWith('.js'))
429
.map(bundle =>
430
`<link rel="preload" href="${bundle.publicPath}" as="script">`
431
)
432
.join('\n');
433
}
434
435
// Usage in server
436
const bundles = getBundles(stats, modules);
437
const preloadLinks = generatePreloadLinks(bundles);
438
439
res.send(`
440
<!DOCTYPE html>
441
<html>
442
<head>
443
${preloadLinks}
444
</head>
445
<body>
446
<!-- ... -->
447
</body>
448
</html>
449
`);
450
```