WHAT
It is a performance pattern, that allows us on import things over network, on some interaction.
This can be done using React.lazy
, Suspense
, Dynamic Import
& facade pattern
.
Instead of Eager Loading
(Loading resources immediately, on page load) , we load them on interaction.
Facade pattern
: It is a design pattern that provides a simplified interface to a more complex subsystem. Means we mimic the UI of the deferred resource using HTML/CSS/JS.
Examples :
-
Showing 3rd Party Widgets
: We load the 3rd party widget on interaction, initially we show a facade.- Video Embeds
- Chat Widgets
- Social Media Widgets
-
Non-critical resources
-
EmojiPicker
: that didn't used that much. -
Filters
-
Authentication SDK's
: Our app supports 3rd party authentication, so loading that on initial render will be a bad idea. Instead import it on interaction - Basically on button click or show a facade to the user.-
Infrequently used features
-
Scroll to Top
: Load animation libraries (e.g., react-scroll) only when the user clicks a "Back to Top" button.
const ScrollToTop = ()=>{ const [scrollLibrary,setScrollLibrary] = useState(null); const handleScroll = ()=>{ import('react-scroll').then((module)=>{ setScrollLibrary(module); module.animateScroll.scrollToTop(); }) } return <> <button onClick={handleScroll}>Back to Top</button> </> }
-
Export
: Defer loading libraries for generating PDFs or CSVs (e.g., jsPDF) until the user clicks an export button. -
Analytics
: Don't preload/prefecth this as that many users don't use it. -
Tabbed Interface
: Load the tab component only when the user clicks a tab.
const Tab1 = React.lazy(()=>import('./Tab1)); const Tab2 = React.lazy(()=>import('./Tab2)); const tabbedInterface = ()=>{ const [activeTab,setActiveTab] = useState(0); return <> <button onClick={()=>setActiveTab(0)}>Tab 1</button> <button onClick={()=>setActiveTab(1)}>Tab 2</button> <Suspense fallback={<div>Loading...</div>}> // In Server side use Loadable Component {activeTab === 0 && <Tab1 />} {activeTab === 1 && <Tab2 />} </Suspense> </>; }
-
HOW
Facade for a Youtube Video Embed
const YoutubeFacade = ()=>{ const [isEmbedLoaded,setIsEmbedLoaded] = useState(false); return <div classname='youtube-embed'> { !isEmbedLoaded ? <> <img src={`https://img.youtube.com/vi/${videoId}/hqdefault.jpg`} alt="Video thumbnail" style={{ width: '100%', height: '100%' }} /> <button style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', padding: '10px 20px', background: 'red', color: 'white', border: 'none', }} > Play </button> </div> </>:<> <iframe width="560" height="315" src={`https://www.youtube.com/embed/${videoId}?autoplay=1`} frameBorder="0" allow="autoplay; encrypted-media" allowFullScreen /> </> } </div> }
Only after the user clicks the facade does the element get added to the DOM with the.
Don't use this when
- you need to do autoplay
- you need that for SEO
Facade for a Login with Google
const GoogleLoginFacade = ()=>{ const [isSdkLoaded,setIsSdkLoaded] = useState(false); const loadGoogleSDK = ()=>{ import("./google-auth").then((module)=>{ // a new bundle is fetched with some autogenerated name, if you want a more friendly then you define its name by doing this - import(/*webpackChunkName:"google-sdk"*/ './google-auth') module.initGoogleSignIn(); setIsSdkLoaded(true); }) } return <> { !isSdkLoaded ? <> <div className='google-login-button'> <button onClick={loadGoogleSDK}>Login with Google</button> </div> </>:<> <div id="google-signin-button" /> </> } </> } // This get loaded on demand // google-auth.js export const initGoogleSignIn = () => { const script = document.createElement('script'); script.src = 'https://apis.google.com/js/client:platform.js?onload=showLoginScreen'; script.async = true; document.body.appendChild(script); };
Top comments (0)