Kshlerin WebStudio πŸš€

React - Display loading screen while DOM is rendering

September 19, 2026

React - Display loading screen while DOM is rendering

Have you ever visited a website and been greeted by a blank screen while waiting for the content to load? In the fast-paced world of web development, user experience is paramount. A jarring loading experience can lead to frustrated users and potentially lost conversions. When building dynamic web applications with React, effectively managing the rendering process is crucial. Displaying a loading screen while the DOM is rendering is a simple yet powerful technique to enhance user engagement and provide a smoother, more professional feel. By implementing a well-designed loading indicator, you can signal to users that your application is actively working and that content is on its way, preventing them from prematurely abandoning your site. This not only improves perceived performance but also contributes to a more polished and user-friendly interface. This article will explore various strategies and best practices for implementing loading screens in your React applications, ensuring a seamless and engaging user experience.

Understanding the Need for Loading Screens in React

React, a popular JavaScript library for building user interfaces, allows developers to create complex, interactive web applications. However, rendering large amounts of data or performing computationally intensive tasks can sometimes lead to delays in displaying content. This delay can be particularly noticeable when fetching data from an external API or rendering complex components. Users are accustomed to near-instantaneous responses, and prolonged loading times can significantly impact their perception of your application’s performance. According to a study by Akamai, 53% of mobile site visitors will leave a page that takes longer than three seconds to load [1]. This statistic underscores the importance of providing visual feedback during loading periods.

A loading screen acts as a visual cue, informing the user that the application is processing data and that the requested content will be displayed shortly. This prevents users from assuming that the application is broken or unresponsive. Moreover, a well-designed loading screen can even enhance the user experience by providing a sense of progress and anticipation. Consider implementing a simple spinner, a progress bar, or even a custom animation that aligns with your brand’s aesthetics. By thoughtfully designing your loading screens, you can transform a potentially frustrating experience into a positive one.

Several factors contribute to the need for loading screens in React applications. Network latency, complex calculations, and large data sets are common culprits. Furthermore, the asynchronous nature of JavaScript means that data fetching and rendering operations may not always occur in a predictable order. By implementing loading screens, you can gracefully handle these asynchronous operations and ensure a consistent user experience, regardless of the underlying factors causing the delay. The key is to identify potential bottlenecks in your application and strategically implement loading indicators to mitigate their impact on the user’s perception of performance.

Implementing Basic Loading Screens in React

The simplest approach to displaying a loading screen in React involves using conditional rendering. You can maintain a state variable that indicates whether the data is currently loading. Initially, set this variable to true. When the data is fetched and ready to be displayed, update the state to false. Based on the value of this state variable, you can conditionally render either the loading screen or the actual content. This approach is straightforward and can be implemented with minimal code.

Here’s a basic example:

import React, { useState, useEffect } from 'react'; function MyComponent() { const [isLoading, setIsLoading] = useState(true); const [data, setData] = useState(null); useEffect(() => { // Simulate data fetching setTimeout(() => { setData({ message: 'Data loaded!' }); setIsLoading(false); }, 2000); }, []); if (isLoading) { return <div>Loading...</div>; } return <div>{data.message}</div>; } export default MyComponent; 

In this example, the isLoading state variable is initially set to true. The useEffect hook simulates data fetching using setTimeout. Once the data is “fetched,” the isLoading state is set to false, and the actual content is rendered. This simple pattern can be adapted to various scenarios, such as fetching data from an API or performing complex calculations. Remember to replace the placeholder loading text with a more visually appealing loading indicator for a better user experience.

Advanced Loading Screen Techniques

While basic conditional rendering is effective, more sophisticated loading screen techniques can further enhance the user experience. One such technique is using React Suspense and lazy loading. React Suspense allows you to “suspend” the rendering of a component until its dependencies, such as code or data, are loaded. Lazy loading enables you to load components only when they are needed, reducing the initial bundle size and improving the application’s startup time. This combination can significantly improve perceived performance, especially for large and complex applications.

Here’s how you can use React Suspense and lazy loading:

import React, { Suspense, lazy } from 'react'; const MyComponent = lazy(() => import('./MyComponent')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <MyComponent /> </Suspense> ); } export default App; 

In this example, the MyComponent is lazily loaded using the lazy function. The Suspense component wraps MyComponent and provides a fallback prop, which specifies the loading indicator to display while MyComponent is being loaded. This approach allows you to seamlessly integrate loading screens into your application without manually managing state variables. Furthermore, React Suspense can be used with data fetching libraries like Relay and Apollo Client to automatically handle loading states for data dependencies. According to the React documentation, Suspense is not yet supported for server-side rendering [2]; however, client-side usage can significantly improve the user experience.

Infographic here
Best Practices for Designing Effective Loading Screens ------------------------------------------------------

Designing an effective loading screen goes beyond simply displaying a spinner. Consider the following best practices to create a loading experience that is both informative and engaging:

  • Use meaningful animations: Replace generic spinners with animations that reflect your brand or the type of content being loaded.
  • Provide progress indicators: If possible, show a progress bar or percentage to give users an estimate of how long the loading process will take.
  • Keep it short and sweet: Avoid overly complex or lengthy loading screens. Aim for a loading time of no more than a few seconds.

According to Nielsen Norman Group, users are more tolerant of delays if they are provided with clear feedback and a sense of progress [3]. A simple progress bar can significantly improve the perceived loading time, even if the actual loading time remains the same. Furthermore, consider using micro-interactions or subtle animations to keep users engaged during the loading process. These small details can make a big difference in the overall user experience. For example, a loading screen might display a subtle animation of your logo or a playful message that reinforces your brand identity. The goal is to transform the loading screen from a passive waiting period into an active and engaging experience.

Consider these points when designing your loading screens:

  • Brand Consistency: Ensure the loading screen aligns with your overall brand aesthetic.
  • Accessibility: Make sure the loading screen is accessible to users with disabilities, including providing alternative text for images and ensuring sufficient color contrast.
  • Performance: Optimize the loading screen to minimize its own loading time. Avoid using large images or complex animations that can slow down the rendering process.

By following these best practices, you can create loading screens that are not only functional but also contribute to a positive and engaging user experience. Remember that the loading screen is often the first impression users have of your application, so it’s important to make it a good one.

Optimizing Performance and User Experience

While displaying a loading screen improves the user experience, optimizing the underlying performance of your React application is equally important. Addressing performance bottlenecks can reduce the need for loading screens altogether. Several strategies can be employed to optimize performance, including code splitting, memoization, and efficient data fetching. Code splitting involves breaking your application into smaller bundles that are loaded on demand, reducing the initial load time. Memoization is a technique for caching the results of expensive function calls, preventing redundant computations. Efficient data fetching involves optimizing API requests and data transformations to minimize the amount of data transferred and processed.

Here’s a step-by-step guide to improve performance:

  1. Analyze Performance: Use browser developer tools to identify performance bottlenecks in your application.
  2. Implement Code Splitting: Break your application into smaller bundles using tools like Webpack or Parcel.
  3. Utilize Memoization: Use React.memo or useMemo to cache the results of expensive computations.
  4. Optimize Data Fetching: Use techniques like pagination, filtering, and data compression to minimize the amount of data transferred.
  5. Debounce and Throttle: Limit the frequency of expensive operations like API calls or event handlers.

In addition to these strategies, consider using a Content Delivery Network (CDN) to serve static assets like images and JavaScript files. A CDN can significantly reduce latency by serving content from servers located closer to the user. Furthermore, optimize your images for the web by compressing them and using appropriate file formats. Large images can significantly impact loading times, so it’s important to ensure that they are properly optimized. By combining these performance optimization techniques with well-designed loading screens, you can create a React application that is both fast and engaging. This approach ensures a smooth user experience, even when dealing with complex data or computationally intensive tasks.

The key takeaway is that displaying a loading screen while the DOM is rendering is crucial for a positive user experience. By showing an indicator, you manage user expectations and prevent frustration during loading times. Optimizing React application performance is a continuous process, and displaying loading screens is an important part of that process, contributing significantly to user satisfaction.

FAQ: Loading Screens in React

**Q: How do I choose the right type of loading indicator?**
A: Consider your brand and the type of content being loaded. Simple spinners are suitable for generic loading, while progress bars are better for indicating progress. Use animations that align with your brand to create a more engaging experience.
**Q: Can I use a custom loading component?**
A: Yes, you can create custom loading components that match your application's design. Ensure the component is lightweight and optimized for performance.
**Q: How do I test my loading screens?**
A: Simulate slow network connections using browser developer tools to test your loading screens under realistic conditions. Ensure the loading screen displays correctly and disappears when the content is loaded.
**Q: What are some common mistakes to avoid when implementing loading screens?**
A: Avoid using overly complex animations that can slow down the loading process. Also, ensure the loading screen is accessible and provides clear feedback to the user.
Implementing effective loading screens in your React applications is more than just a technical task; it's an investment in user satisfaction. By understanding the nuances of rendering performance and employing strategies like conditional rendering, Suspense, and lazy loading, you can create a seamless and engaging experience for your users. Remember to prioritize performance optimization and design loading screens that are both informative and visually appealing. Explore different types of loading indicators, such as spinners, progress bars, and custom animations, and choose the ones that best reflect your brand and the type of content being loaded. And don't forget to test your loading screens thoroughly to ensure they function correctly under various network conditions. Ready to dive deeper and refine your approach? Explore these advanced techniques to elevate your React development skills and ensure your users always have a smooth and enjoyable experience. Check out [our guide on advanced React state management](https://courthousezoological.com/n7sqp6kh?key=e6dd02bc5dbf461b97a9da08df84d31c) for even more tips!

Question & Answer :
This is an example from the Google AdSense application page. The loading screen is displayed before the main page is shown.

enter image description here

I am not sure how to achieve the same effect with React because if I render the loading screen as a React component, it will not be displayed while the page is loading as it needs to wait for the DOM to be rendered first.

Updated:

As a solution, I tried an approach where I placed the screen loader in index.html file and removed it in the componentDidMount() lifecycle method of React component.

Example and react-loading-screen.

The goal

When the html page is rendered, display a spinner immediately (while React loads), and hide it after React is ready.

Since the spinner is rendered in pure HTML/CSS (outside of the React domain), React shouldn’t control the showing/hiding process directly, and the implementation should be transparent to React.

Solution 1 - the :empty pseudo-class

Since you render react into a DOM container - <div id="app"></div>, you can add a spinner to that container, and when react will load and render, the spinner will disappear.

You can’t add a DOM element (a div for example) inside the react root, since React will replace the contents of the container as soon as ReactDOM.render() is called. Even if you render null, the content would still be replaced by a comment - \<!-- react-empty: 1 -->. This means that if you want to display the loader while the main component mounts, data is loading, but nothing is actually rendered, a loader markup placed inside the container (<div id="app"><div class="loader"></div></div> for example) would not work.

A workaround is to add the spinner class to the react container, and use the :empty pseudo class. The spinner will be visible, as long as nothing is rendered into the container (comments don’t count). As soon as react renders something other than comment, the loader will disappear.

Example 1

In the example you can see a component that renders null until it’s ready. The container is the loader as well - <div id="app" class="app"></div>, and the loader’s class will only work if it’s :empty (see comments in code):

``` class App extends React.Component { state = { loading: true }; componentDidMount() { // this simulates an async action, after which the component will render the content demoAsyncCall().then(() => this.setState({ loading: false })); } render() { const { loading } = this.state; if(loading) { // if your component doesn't have to wait for an async action, remove this block return null; // render null when app is not ready } return (
I'm the app
); } } function demoAsyncCall() { return new Promise((resolve) => setTimeout(() => resolve(), 2500)); } ReactDOM.render( , document.getElementById('app') ); ```
.loader:empty { position: absolute; top: calc(50% - 4em); left: calc(50% - 4em); width: 6em; height: 6em; border: 1.1em solid rgba(0, 0, 0, 0.2); border-left: 1.1em solid #000000; border-radius: 50%; animation: load8 1.1s infinite linear; } @keyframes load8 { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script> <div id="app" class="loader"></div> <!-- add class loader to container -->
**Example 2**

A variation on using the :empty pseudo class to show/hide a selector, is setting the spinner as a sibling element to the app container, and showing it as long as the container is empty using the adjacent sibling combinator (+):

``` class App extends React.Component { state = { loading: true }; componentDidMount() { // this simulates an async action, after which the component will render the content demoAsyncCall().then(() => this.setState({ loading: false })); } render() { const { loading } = this.state; if(loading) { // if your component doesn't have to wait for async data, remove this block return null; // render null when app is not ready } return (
I'm the app
); } } function demoAsyncCall() { return new Promise((resolve) => setTimeout(() => resolve(), 2500)); } ReactDOM.render( , document.getElementById('app') ); ```
#app:not(:empty) + .sk-cube-grid { display: none; } .sk-cube-grid { width: 40px; height: 40px; margin: 100px auto; } .sk-cube-grid .sk-cube { width: 33%; height: 33%; background-color: #333; float: left; animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out; } .sk-cube-grid .sk-cube1 { animation-delay: 0.2s; } .sk-cube-grid .sk-cube2 { animation-delay: 0.3s; } .sk-cube-grid .sk-cube3 { animation-delay: 0.4s; } .sk-cube-grid .sk-cube4 { animation-delay: 0.1s; } .sk-cube-grid .sk-cube5 { animation-delay: 0.2s; } .sk-cube-grid .sk-cube6 { animation-delay: 0.3s; } .sk-cube-grid .sk-cube7 { animation-delay: 0s; } .sk-cube-grid .sk-cube8 { animation-delay: 0.1s; } .sk-cube-grid .sk-cube9 { animation-delay: 0.2s; } @keyframes sk-cubeGridScaleDelay { 0%, 70%, 100% { transform: scale3D(1, 1, 1); } 35% { transform: scale3D(0, 0, 1); } }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script> <div id="app"></div> <!-- add class loader to container --> <div class="sk-cube-grid"> <div class="sk-cube sk-cube1"></div> <div class="sk-cube sk-cube2"></div> <div class="sk-cube sk-cube3"></div> <div class="sk-cube sk-cube4"></div> <div class="sk-cube sk-cube5"></div> <div class="sk-cube sk-cube6"></div> <div class="sk-cube sk-cube7"></div> <div class="sk-cube sk-cube8"></div> <div class="sk-cube sk-cube9"></div> </div>
---

Solution 2 - Pass spinner “handlers” as props

To have a more fine grained control over the spinners display state, create two functions showSpinner and hideSpinner, and pass them to the root container via props. The functions can manipulate the DOM, or do whatever needed to control the spinner. In this way, React is not aware of the “outside world”, nor needs to control the DOM directly. You can easily replace the functions for testing, or if you need to change the logic, and you can pass them to other components in the React tree.

Example 1

``` const loader = document.querySelector('.loader'); // if you want to show the loader when React loads data again const showLoader = () => loader.classList.remove('loader--hide'); const hideLoader = () => loader.classList.add('loader--hide'); class App extends React.Component { componentDidMount() { this.props.hideLoader(); } render() { return (
I'm the app
); } } // the setTimeout simulates the time it takes react to load, and is not part of the solution setTimeout(() => // the show/hide functions are passed as props ReactDOM.render( , document.getElementById('app') ) , 1000); ```
.loader { position: absolute; top: calc(50% - 4em); left: calc(50% - 4em); width: 6em; height: 6em; border: 1.1em solid rgba(0, 0, 0, 0.2); border-left: 1.1em solid #000000; border-radius: 50%; animation: load8 1.1s infinite linear; transition: opacity 0.3s; } .loader--hide { opacity: 0; } @keyframes load8 { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script> <div id="app"></div> <div class="loader"></div>
**Example 2 - hooks**

This example uses the useEffect hook to hide the spinner after the component mounts.

``` const { useEffect } = React; const loader = document.querySelector('.loader'); // if you want to show the loader when React loads data again const showLoader = () => loader.classList.remove('loader--hide'); const hideLoader = () => loader.classList.add('loader--hide'); const App = ({ hideLoader }) => { useEffect(hideLoader, []); return (
I'm the app
); } // the setTimeout simulates the time it takes react to load, and is not part of the solution setTimeout(() => // the show/hide functions are passed as props ReactDOM.render( , document.getElementById('app') ) , 1000); ```
.loader { position: absolute; top: calc(50% - 4em); left: calc(50% - 4em); width: 6em; height: 6em; border: 1.1em solid rgba(0, 0, 0, 0.2); border-left: 1.1em solid #000000; border-radius: 50%; animation: load8 1.1s infinite linear; transition: opacity 0.3s; } .loader--hide { opacity: 0; } @keyframes load8 { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="app"></div> <div class="loader"></div>