<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Taran J Singh on Medium]]></title>
        <description><![CDATA[Stories by Taran J Singh on Medium]]></description>
        <link>https://medium.com/@taranjsingh?source=rss-5a6055ceff70------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*SMZYnaT_JI0Nw_WE15s7hA.png</url>
            <title>Stories by Taran J Singh on Medium</title>
            <link>https://medium.com/@taranjsingh?source=rss-5a6055ceff70------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 18:57:07 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@taranjsingh/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Code Chunks with React Lazy and Suspense]]></title>
            <link>https://medium.com/@taranjsingh/code-chunks-with-react-lazy-and-suspense-d21506fe0db1?source=rss-5a6055ceff70------2</link>
            <guid isPermaLink="false">https://medium.com/p/d21506fe0db1</guid>
            <category><![CDATA[react-performance]]></category>
            <category><![CDATA[react-lazy]]></category>
            <category><![CDATA[single-page-applications]]></category>
            <category><![CDATA[react-suspense]]></category>
            <category><![CDATA[code-splitting]]></category>
            <dc:creator><![CDATA[Taran J Singh]]></dc:creator>
            <pubDate>Wed, 01 May 2019 07:21:00 GMT</pubDate>
            <atom:updated>2019-05-01T07:21:00.860Z</atom:updated>
            <content:encoded><![CDATA[<h3>Split Code with React Lazy + Suspense API</h3><h4>How to, why to… and when not to !</h4><p>React 16.6 introduced new API’s for <a href="https://reactjs.org/docs/code-splitting.html">client side code splitting</a>. We will analyse our options of code splitting using these API’s — Route &amp; Component based.</p><blockquote>This article assumes that you already have some experience with creating React apps and are well versed with web-cycle of SPAs</blockquote><h4>Table of contents</h4><ol><li>Before you start — Benefits of CS</li><li>Intro to Lazy &amp; Suspense API</li><li>Let’s SPLIT</li><li>When not to code split</li></ol><h3>1) Before you start — Benefits of CS</h3><p>No matter how much we advocate SPA’s, there a fundamental problem with SPAs: excessive size of the bundle- that needs to load before browser is able to render the application. Even the smallest boilerplate applications are bound to be around <a href="http://elijahmanor.com/react-file-size/">100KB</a>. And as we start adding our logic, components, styles, behaviours, it usually shoots to MBs. Then there are additional performance lags due to client-side application’s lifecycle (web-cycle) — download javascript bundles; initialise javascript code on browser; fetch data, config &amp; content specific to route/context; and finally render the UI (did we miss CSS?).</p><p>There are quite a few ways to solve performance lags of client-side javascript applications. One of the best possibility lies with <a href="https://medium.com/@baphemot/whats-server-side-rendering-and-do-i-need-it-cb42dc059b38">Server-Side Rendering</a>. However, it requires us to switch to a node server, or cache the pre-rendered HTML’s. And if you don’t intend to do SSR, your other best resolve would be code-splitting. Let’s have a look at a single bundle SPA vs. a code split SPA.</p><p><strong>1.1 Single Bundle SPA</strong></p><p>Here’s a <a href="http://swevens.art/formik-yup-react-single/">sample-app-1</a>, hosted on <a href="http://swevens.art/formik-yup-react-single/">GitHub pages</a>. The javascript file size is 1.2MB (non gzipped), with everything compiled inside single javascript bundle file. Below are some metrics from console, bundle analyser and Lighthouse on this app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zxILu8ckZb5yQe457TnZug.png" /><figcaption>build output from console</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OhlM345KvYAMMSzVxnyc5A.png" /><figcaption>build stats output using Webpack bundle analyser (ignore the hash, I snapped the images at different build runs)</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yHWJORbmKAynrkT0bun5pQ.png" /><figcaption>Lighthouse report of the above application hosted on Github Pages</figcaption></figure><p><strong>1.2 Code Split SPA</strong></p><p>SPA’s with multiple output files via code splitting enables us to dynamically load only what’s required, when it is required. Code splitting emits multiple code files — chunks, which can be requested and loaded in browser, conditionally. This improves the page load time, first paint time, and even time to interactive, hence rendering our application faster and making them interactive sooner.</p><p>Let’s look at the stats, webpack bundle analyser &amp; lighthouse audit for the same app, with code splitting, <a href="http://swevens.art/formik-yup-react-multiple/">sample-app-2</a>, hosted on <a href="http://swevens.art/formik-yup-react-single/">GitHub pages</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-Q6lh_SoRMhtSScIffkR7g.png" /><figcaption>build output from console</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OqZ7-y21RRjhG2YWBr0R-w.png" /><figcaption>build stats output using Webpack bundle analyser (ignore the hash, I snapped the images at different build runs)</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jGOsMWSfawEg4aBBzfdEvg.png" /><figcaption>Lighthouse report of the code split application hosted on <a href="http://swevens.art/formik-yup-react-single/">Github pages</a>:</figcaption></figure><p>Voila, the metrics have improved by <strong><em>.6 seconds; from 1s to .4s</em></strong> on an average for all the factors, by doing code splitting. We can also observe that application is visible 2 frames earlier to end user, in the lighthouse audit.</p><p>On top of performance improvements, there is a chunk ‘<em>4</em>’ completely dedicated to material-ui. I have intentionally included this module and imported it in the project app, and isn’t used in regular application flow. In the case without code-splitting, this code is always loaded. Whereas with code-splitting it will only be loaded if required condition is met, which is optional to completion of user’s booking journey. So if your SPA has multiple paths, like material-ui being used on a screen which is optional to booking flow, code-splitting and can help us to load the code based on user’s application usage.</p><blockquote>If your SPA has multiple user journey paths, code-splitting and can help us to load the code based on user’s journey and application usage.</blockquote><h3>2) Intro to Lazy &amp; Suspense API</h3><p>Code splitting isn’t new, <a href="https://reactjs.org/docs/code-splitting.html">Lazy &amp; Suspense</a> are built on top of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Import">dynamic import</a>, which exists from a while and is already built into webpack. However what these API really do is to standardise code splitting. This new API:</p><p>2.1 can dynamically load code pieces whenever required- at a user action/event, route level, component level, or even after page load complete.</p><p>2.2 has better controlled loading, fall-back &amp; error handling.</p><p>2.3 doesn’t require changes to webpack scripts.</p><p>2.4 has options to load multiple code pieces, imported as separate dynamic imports, on the same event.</p><p>2.5 has an option to pull in named exports as code pieces, with minimal changes.</p><p>2.6 further, the dynamic imports internally optimises and breaks code into further modules, to have common modules as separate chunks. Out of code pieces A, B &amp; C, any common code between A &amp; B, or B &amp; C, or C &amp; A, will break out into separate chunks, and react will handle loading of these common chunks by itself on request of A, B or C.</p><blockquote><strong>Disclaimer</strong></blockquote><blockquote><strong>Lazy and suspense API doesn’t work for server-side code splitting yet. It solves only for dynamic loading for client side applications.</strong></blockquote><h3>3) Let’s SPLIT</h3><p>As mentioned before, we can choose to split the code as per route or components… or both. Below are examples for both these ways.</p><p><strong>3.1 Option A — Route Based</strong></p><p>If the SPA is built on top of react-router, easiest way to introduce React.Lazy to the router file. A basic router file looks like this:</p><pre>import CompA from ‘./CompA’;<br>import CompB from ‘./CompB’;<br>import CompC from ‘./CompC’;</pre><pre>/* Inside React component&#39;s render method */<br>&lt;Switch&gt; <br> &lt;Route path=’/CompA’ component={CompA}/&gt; <br> &lt;Route path=’/CompB’ component={CompB}/&gt;<br> &lt;Route path=’/CompC’ component={CompC}/&gt; <br>&lt;/Switch&gt;</pre><p>Instead of the regular import from file, we will use the React.Lazy to dynamically import the module, and wrap our components with React.Suspense &amp; an Error Boundary as below:</p><pre>import React, { Suspense, lazy } from &#39;React&#39;;</pre><pre>const CompA = lazy(() =&gt; import(‘./CompA’));<br>const CompB = lazy(() =&gt; import(‘./CompB’));<br>const CompC = lazy(() =&gt; import(‘./CompC’));</pre><pre>/* Inside React component&#39;s render method */</pre><pre>/* ErrorBoundary is to render fallback ui incase of errors */  &lt;ErrorBoundary&gt;<br> &lt;Suspense fallback={&lt;div&gt;Loading…&lt;/div&gt;}&gt;<br>    &lt;Switch&gt; <br>     &lt;Route exact path=’/’ component={LoaderHOC(CompA)}/&gt; <br>     &lt;Route path=’/CompB’ component={LoaderHOC(CompB)}/&gt;<br>     &lt;Route path=’/CompC’ component={LoaderHOC(CompC)}/&gt; <br>    &lt;/Switch&gt;<br>  &lt;/Suspense&gt;<br>&lt;/ErrorBoundary&gt;</pre><p>The code uses Lazy, Suspense &amp; Error Boundaries as detailed below:</p><ol><li>React.Lazy — accepts a function that must call dynamic import. This must return a Promise that resolves to a module with a default export containing a React component. This will automatically load the bundle containing the ‘CompA’ when the route matches to ‘/CompA’, and so on. The dynamic code syntax instructs webpack to split in separate chunks &amp; tree shaking optimises chunks — depending on the code-structure and components shared between dynamically imported modules, it may even split it into multiple files per route. Loading these additional chunks per route is all taken care by React and Webpack.</li><li>React.Suspense — will display a fall-back component, while loading the requested module in browser. We can also extend the ‘fall-back’ to be an animated pre-loader… or as I did, use the header navigation component and a <a href="https://www.npmjs.com/package/react-loading-skeleton">skeletal</a>. The fall-back component becomes part of the main chunk, or even a separate chunk based on your code structure.</li><li><a href="https://reactjs.org/docs/error-boundaries.html">ErrorBoundary</a> — is another HOC that is used to capture any run-time code errors inside the child modules, or even network errors if the module is not loaded. A sample error boundary wrapper to check for network errors would look like:</li></ol><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fc0f028413a1c81a88e56e012684ba2b/href">https://medium.com/media/fc0f028413a1c81a88e56e012684ba2b/href</a></iframe><p>That’s it. We can run a <a href="https://medium.com/@romanonthego/webpack-bundle-analyzer-for-create-react-app-9aebb0d01084">bundle-analyser</a> or inspect in the browser’s network panel and see this in action. I would recommend to run a bundle-analyser on your code, and see if there is any further optimisation you can figure out. For best results, remove any unused imports from each and every file in your code base.</p><p>Check out a sample demo with code of Route Level Code splitting with Lazy/Suspense on the same sample app we analysed before in step 1 — sample-app-3.</p><p><strong>3.2 Option B — Component Based</strong></p><p>As per Code Splitting, loading the next required code piece is dependent on a Decision-Making ‘event’. As seen above, with Route based code splitting, this decision event is tagged to the Application Route, and implemented using React’s Router. With Component based chunks, we can have flexibility to tag this event to various types of actions — scrolling of the page or any other user interaction action, any internal app lifecycle action like config or data, or even based on user’s activity on the app.</p><p>Let’s consider use-case of a multi-step booking SPA. The next view of the application depends on whether user selects one way journey or return journey. As such, we can check user’s selection and dynamically load next component. This also helps us to avoid loading pieces of code, that are not required for a particular user, as per the choices he makes in the application flow.</p><p>To do this, we would need to write a function (<em>getViewComponent</em>), that accepts the component identifier and returns the react component. This method is almost similar to ‘<em>LoaderHOC</em>’ written above for route level splitting, except that it accepts a string name instead of the component itself, to enable dynamic request for components by string names.</p><pre>import React, { Suspense, lazy } from &#39;React&#39;;</pre><pre>const AOneWay = lazy(() =&gt; import(‘./CompAOneWay’));<br>const AReturn = lazy(() =&gt; import(‘./CompAReturn’));<br>const BOneWay = lazy(() =&gt; import(‘./CompBOneWay’));<br>const BReturn = lazy(() =&gt; import(‘./CompBReturn’));<br>const MapChunk = {AOneWay, AReturn, BOneWay, BReturn};</pre><pre>const getViewComponent = (view) =&gt; {<br> const Element = MapChunk[view];<br> return <br>  &lt;ErrorBoundary&gt;<br>   &lt;Suspense fallback={&lt;div&gt;Loading…&lt;/div&gt;}&gt;<br>     &lt;Element /&gt;<br>   &lt;/Suspense&gt;<br>  &lt;ErrorBoundary&gt;<br>};</pre><pre>class Main extends React.Component {<br> changeView(name) {<br>  if(this.state.isReturnJourney) {<br>    this.setState({currentView, name + ‘OneWay’});<br>  }<br>  else {<br>    this.setState({currentView, name + ‘Return’});<br>  }<br> }</pre><pre> onNextClick(e) {<br>  this.changeView(‘CompB’);<br> }</pre><pre> render() {<br>  &lt;div&gt; {getViewComponent(this.state.currentView)} &lt;/div&gt;<br> }<br>}</pre><p>The code uses Lazy, Suspense &amp; Error Boundaries in a same way we used them for route level chunks. It additionally uses MapChunk, getViewComponent &amp; the Main Class’s internal state, as below:</p><ol><li>MapChunk — is a simple javascript object, that keeps a map of all react component references. Using the React Component variables in this way, doesn’t load them into browser, and still maintains out Lazy+Suspense lifecycle.</li><li>getViewComponent — is simply an engine that picks up string name, and converts it into React Component. It refers to MapChunk, and uses Lazy+Suspense to load the required module file to browser, if it isn’t loaded already.</li><li>Main State Variable — we use this state variable to keep maintain the view. This needs to be maintained in your topmost container inside Provider. The currentView variable holds the current set state of the application derived from user’s action. It is updated to next view, whenever user performs the next event.</li></ol><p>And that’s it. Check out a <a href="http://swevens.art/formik-yup-react-forms/">sample demo</a> with <a href="https://github.com/swevens/formik-up-react-forms">code</a> of Component Level Code splitting with Lazy/Suspense. You can run the bundle analyser provided in the sample codebase and see the results. To compare, simply change React.Lazy imports to regular imports, and you’ll see output as a single file.</p><h3>4. When not to split!</h3><p>After such a long article promoting code splitting and looking at its benefits, you may wonder if there could be any drawback at all? But there is always a catch. There are some scenarios worth considering before moving into the world of Client Side Code-Splitting:</p><p><strong>4.1 Delayed initialisation</strong></p><p>Let’s look at the network panel of code-split application, we observed earlier.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YASvAM3rLHzps-WMrMnU0w.png" /></figure><p>The problem here is, the primary chunk, that is injected to the html page, will download, initialise and then request additional chunks. This delays the Document Load event, because substantial amount of javascript has to be executed, before it can request additional modules. So it’s better to ensure you keep your primary or main entry route to SPA, or main components, in the main chunk. Or play around with dynamic imports as per applications web-cycle and analyse results.</p><p>There are couple of more ways to fix it, by identifying the route at server and injecting all the files required for first print for each route on the page itself to avoid this behaviour. But then we step into realms of SSR, and it’s not out-of-the-box implemented by this API. So, if your code split bundle output requires you to have chain of javascript files to load… its maybe better to not do code splitting. Run some tests and figure out what works better for your use case.</p><p><strong>4.2 Caching problems</strong></p><p>Another set of issue is with caching of code split chunks — To improve performance, a best practice is to add caching to all resources, including HTML page &amp; all JS files. With chunks, we usually put a hash on file names: to serve latest files on re-deployments. With html file being cached, browser will always look for the same hash javascript files.</p><p>The simplest way to avoid caching issues is to remove caching from html file, and use caching only on javascript files with hash keys. So it will always load the new HTML file with updated hash-keys. And even if there is no new deployments before a user re-access the application, the javascript files will be picked from browser’s cache, as hash key remains same in HTML.</p><p>There are other nuances with caching with Code Splitting, so ensure you validate performance and see if code splitting is really effective with your caching strategy.</p><p><strong>In the end, thank you for reading, shout out questions in comments if you have any.</strong></p><h3><strong>References</strong></h3><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d21506fe0db1" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>