How to use React Suspense for Code-Splitting?
Tags: react, typescript, javascript, webpack
Published: 2020-04-18 08:45:49

---
title: How to use React Suspense for Code-Splitting?
published: true
description: Suspense allows React to suspend rendering while waiting for something.
series: React Suspense for Code-Splitting 
tags: #react #typescript #javascript #webpack
---
React 16.6 shipped an interesting feature called `Suspense`. `Suspense` allows React to suspend rendering while waiting for something. Loading indicator is displayed during wait time.

This post shows an example of how I used suspense on my simple blog site <a href="https://markomarinovic.com">markomarinovic.com </a>.

## Why is this important to us?
We bundle our React apps using tools like <a href="https://webpack.js.org/">webpack</a> and <a href="https://rollupjs.org">rollup</a>. 
Our bundle grows as our application grows, especially when we include different third party libraries. Over time, our bundle will be huge and impact loading time of our application. 
To prevent this from happening, we can start splitting our big bundle into multiple smaller bundles with a process called `code-splitting`. Code-Splitting is supported by tools such as `webpack` and `rollup`. These tools can generate multiple bundles that we can load on demand in runtime. I'll focus on `webpack` since it's used in this project.

With code-splitting we can give to the user only the code he currently needs, loading additional code as user navigates though the app.

## How can Suspense and lazy help us?
`Suspense` will display the fallback component while bundle is dynamically loaded. It works together with `lazy` function. 

`lazy` takes a single argument called `factory` (function returning a promise) and creates a new component of type `LazyComponent`. `LazyComponent` will call a `factory` function on the first render and resulting promise will be used by `Suspense` to show/hide the fallback component. In the case of code-splitting, we will pass a dynamic import of our component module. 

```jsx
import { lazy } from 'React';

const Home = lazy(() => import('./pages/Home'));
const BlogDetails = lazy(() => import('./pages/BlogDetails'));
```

`webpack` will see a dynamic import and create a promise that will resolve once the bundle is loaded. You can read more about `webpack` dynamic imports <a href="https://webpack.js.org/guides/code-splitting/#dynamic-imports" target="_blank">here</a>.

Keep in mind that `lazy` only supports default exports, so make sure your component module has a default export. If you have a named export, you can re-export it as default to work around this.

## Lazy loading pages and blog content
This is the `App.ts` for this blog project. Each page is dynamically loaded the first time we navigate to it.
```jsx
import React, { Suspense, lazy } from 'react';
import { Router } from '@reach/router';
import { ErrorBoundary } from 'shared/components/ErrorBoundary';
import SuspenseFallback from 'shared/components/SuspenseFallback';
import { posts } from 'data/blog';

const Home = lazy(() => import('./pages/Home'));
const BlogDetails = lazy(() => import('./pages/BlogDetails'));

const App = () => (
  <React.StrictMode>
    <ErrorBoundary>
      <Suspense fallback={<SuspenseFallback />}>
        <Router>
          <Home path="/" />
          {posts.map(post => {
            const { id, slug } = post;
            return <BlogDetails post={post} key={id} path={`/${slug}`} />;
          })}
        </Router>
      </Suspense>
    </ErrorBoundary>
  </React.StrictMode>
);

export default App;

```

`<SuspenseFallback />` will be displayed while we wait for the bundle to load. You can test this by throttling your internet connection and refreshing the page.

```jsx
import React from 'react';

const SuspenseFallback = () => (
  <span>Suspended. Loading data...</span>
);

export default SuspenseFallback;
```

## Lazy loading blog post content
The interesting thing is that we don't have to use this concept just for routing. Lazy loading is used in this blog project for content fetching as well.
Content for each blog post is in the form of `.mdx` file. 

`BlogDetails` component will be loaded the first time we click on the blog post. Each blog post has a separate content which will be loaded separately.
This allows us to load `BlogDetails` component once and separately load content depending on the post. 
Without lazy loading the content, we would have to bundle all `.mdx` files in the main bundle, drastically increasing our bundle size. `mdx` files replace database calls in this case.

Blog post data looks like this:

```jsx
import { lazy } from 'react';

interface IBlogPost {
  id: number;
  title: string;
  description: string;
  date: string;
  slug: string;
  Content: React.LazyExoticComponent<any>;
}

const post: IBlogPost = {
  id: 4,
  title: 'How to use React Suspense for Code-Splitting?',
  description: 'Suspense allows React to suspend rendering while waiting for something.',
  date: '10.02.2020 @ 21:30',
  slug: 'how-to-use-react-suspense-for-code-splitting',
  Content: lazy(() => import('./content.mdx')),
};

export default post;
```
`Content` is our lazy component which dynamically imports `content.mdx` file.

`BlogDetails` component renders lazy `Content` component, initiating `.mdx` file load. 
```js
import React from 'react';
import { RouteComponentProps } from '@reach/router';
import Layout from 'shared/components/Layout';
import { StyledHeader } from 'shared/styles/components';
import { IBlogPost } from 'shared/types/models/blog';
import MDXWrapper from 'shared/wrappers/MDXProvider';
import { StyledContent, StyledPublishedOn } from './BlogDetailsStyles';

interface IOwnProps {
  post: IBlogPost;
}

type IProps = IOwnProps & RouteComponentProps;

const BlogDetails = (props: IProps) => {
  const { post } = props;
  const { title, date, description, slug, Content } = post;
  const pageMeta = {
    title,
    description,
    slug,
    date,
  };

  return (
    <Layout meta={pageMeta}>
      <StyledHeader>
        <h1>{title}</h1>
        <StyledPublishedOn>Published on {date}</StyledPublishedOn>
      </StyledHeader>
      <StyledContent>
        <MDXWrapper>
          <Content />
        </MDXWrapper>
      </StyledContent>
    </Layout>
  );
};

export default BlogDetails;
```

If you open the network tab in dev tools you will see that the first time you visit a blog post it loads multiple bundles. 
Visiting other blog posts will initiate a load of additional `content.mdx` bundles.

## Conclusion
`Suspense` for code-splitting is a very powerful tool for improving application performance. This is something you can start implementing right now to greately improve performance of your web app. There are more `Suspense` related things comming in the new <a href="https://reactjs.org/docs/concurrent-mode-intro.html" target="_blank">concurrent mode</a> for React which is currently in experimental phase.