How to implement loadable components for bundle splitting with SSR support.
Published: 2020-05-31 20:24:36

Before Jumping into code spitting using loadable lets understand some concepts:- 
<h1><u><i>Client-side rendering vs Server-side rendering</i></u></h1>

In Client-Side Rendering, browser downloads a minimal HTML page, then it renders the JavaScript and fills the content into it.

In Server-side rendering, on the other hand, browser renders components on the server. output is HTML which is already filled with content, so there is no need to fetch any JS. 
After that, we need a mechanism to hydrate it on the client-side with your JS and make it functional.

<h1><u><i>Choosing the right library</i></u></h1>
# **1. React.lazy and Suspense**

React.lazy provides syntax on top of your bundler’s code splitting (webpack or parcel) and call import when it’s getting rendered within the component.

The lazy component should then be rendered inside a Suspense component, which allows us to show some fallback content

- React.lazy supports SSR
- but Suspense does **NOT**.

Suspense is not available server-side and React.lazy can only work with Suspense. That's why today, React.lazy is not an option if you need Server Side Rendering.

# **2. Loadable components**

@loadable/component provides a complete solution to make Server Side Rendering possible and takes care of synchronously loading components

```javascript
import loadable from “@loadable/components“;
const MyComponent = loadable(() => import(“../my/component/path.js”), 
{ssr: true});
<MyComponent /> // Just render your Component 
```

<MyComponent /> here will have its separate javascript bundle and its done using module bundler
there is option to pass object with SSR as key to choose wether you can your component to SSRed or not

If its true, then component gets rendered on the server and then we can hydrate the functionality on the client-side by executing the injected JavaScript from **loadable-stats.json**





<h1><u><i>Now lets get into Steps</h1></u></i>

# **1. Install `@loadable/babel-plugin`**

**.babelrc**

```javascript
{
  "plugins": ["@loadable/babel-plugin"]
}
```
If you don’t want to follow this step, you can add this in the webpack config file where you have plugins for babel defined.


# **2. Install `@loadable/webpack-plugin`**


# **3. Create a webpack loadable plugin.**

**webpack.config.js**

```javascript
const LoadablePlugin = require('@loadable/webpack-plugin')
module.exports = {
  // ...
  plugins: [new LoadablePlugin()],
}

```
new LoadablePlugin({ filename: 'stats.json', writeToDisk: true })

# **loadable-stats.json**

It maintains mapping of all the components by chunk name.

It informs loadable about the component dependencies. It also tells about all the CSS and JS bundles required for that component.

# **4. Setup ChunkExtractor server-side**

ChunkExtractor is a constructor function, that takes loadable-stats.json file as argument and return instance of function of your stats file.

It takes your main app component to this instance and fetch the chunks required for this component.

The chunks we extract using “extractor.collectChunks” is the JSX that will be passed to the renderToString function.

# Collecting chunks

The basic API goes as follows:

```javascript
import { renderToString } from 'react-dom/server'
import { ChunkExtractor } from '@loadable/server'
const statsFile = path.resolve('../dist/loadable-stats.json')
const extractor = new ChunkExtractor({ statsFile })
const html = renderToString(extractor.collectChunks(<YourApp />))
const scriptTags = extractor.getScriptTags() // or extractor.getScriptElements();

```

The `collectChunks` method wraps your element in a provider. Optionally you can use the `ChunkExtractorManager` provider directly, instead of this method. Just make sure not to use it on the client-side.

**getScriptTags-** returns a string of script tags which are “async”.

**getStyleTags-** returns a string of link elements with the “data-chunk” attribute set to the component’s chunk name.

```javascript
import { renderToString } from 'react-dom/server'
import { ChunkExtractor, ChunkExtractorManager } from '@loadable/server'
const statsFile = path.resolve('../dist/loadable-stats.json')
const extractor = new ChunkExtractor({ statsFile })
const html = renderToString(
  <ChunkExtractorManager extractor={extractor}>
    <YourApp />
  </ChunkExtractorManager>,
)
const scriptTags = extractor.getScriptTags() // or extractor.getScriptElements();

```

The `extractor.getScriptTags()` returns a string of multiple `<script>` tags marked as "async". You have to wait for them to be ready using `loadableReady`.

Alternatively the ChunkExtractor also has a getScriptElements() method that returns an array of React elements.

# **5. Add `loadableReady` client-side**

Loadable components loads all your scripts asynchronously to ensure optimal performances. All scripts are loaded in parallel, so you have to wait for them to be ready using `loadableReady`.

```javascript
import { loadableReady } from '@loadable/component'
loadableReady(() => {
  const root = document.getElementById('main')
  hydrate(<App />, root)
})

```

# **6. Fallback without Suspense**

If in case you have loadable({ssr:false}). You would like to have some loading state.

 You can specify a `fallback` in `loadable` options.

```javascript
const OtherComponent = loadable(() => import('./OtherComponent'), {
  fallback: <div>Loading...</div>,
})
function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  )
}

```

You can also specify a `fallback` in props:

```javascript
const OtherComponent = loadable(() => import('./OtherComponent'))
function MyComponent() {
  return (
    <div>
      <OtherComponent fallback={<div>Loading...</div>} />
    </div>
  )
}

```

**THE END**
This was quite the ride, wasn’t it? I hope it was helpful, Do share you feedbacks and suggestions. Thanks YOU !!!