Stop Showing Blank Screens: Understanding React Suspense and Lazy Loading

Ezinne Nwani is a Frontend Developer based in Lagos, Nigeria. She shares her journey through clear, beginner-friendly technical articles on web development.
Have you ever navigated to a page or a dashboard in a React application and seen a blank screen for a few seconds?
No images. No text. Just... nothing.
Or maybe, you have opened a React app and noticed that it loads everything at once, even parts of the app you have not visited.
If you have experienced this, then you have seen what happens when code is loaded inefficiently.
That's where lazy loading and Suspense come in.
They allow us to:
Load only what we need, when we need it
Avoid shipping unnecessary JavaScript upfront
Display a fallback UI while components load
Improve perceived performance
And in real world applications, perceived performance is just as important as actual performance.
What is Lazy Loading?
Lazy loading is a technique where components are loaded only when they are needed, instead of loading everything during the initial render.
By default, React bundles your entire component tree into one large JavaScript file. The larger your app grows, the larger that bundle becomes.
With lazy loading, we split that bundle.
import { lazy } from "react";
const Dashboard = lazy(() => import("./Dashboard"));
Let’s break this down:
lazy()is imported from React.It takes a function that uses a dynamic
import().That dynamic import tells the bundler (like Vite or Webpack) to create a separate chunk.
The component is loaded only when it’s actually rendered.
This is called code splitting.
Instead of forcing users to download your entire application upfront, you let them download only what they need at that moment.
What is Suspense?
Lazy loading alone is not enough.
When a lazy component is being fetched, React needs to show something.
That’s where Suspense comes in.
import { Suspense } from "react";
<Suspense fallback={<p>Loading...</p>}>
<Dashboard />
</Suspense>
The fallback prop defines what users see while the component is loading.
This could be:
A text
A loading spinner
A skeleton screen
A progress bar
A branded loader component
Instead of showing a blank screen, you show intentional UI, and that small detail significantly improves perceived performance.
Why Blank Screens Happen (And Why They're Bad)
Without lazy loading:
Your initial JavaScript bundle grows large.
The browser blocks rendering until it downloads and parses everything.
On slower networks, users wait.
Sometimes they see nothing.
Large dashboards, chart-heavy pages, authentication flows, and route-based apps are especially affected.
But with lazy loading + Suspense:
Your initial bundle is smaller.
Components load progressively.
Users get visual feedback immediately.
The app feels faster.
Performance shouldn't only be about getting a high number in a testing tool, it should also be about how fast the app feels.
An Example using Lazy Loading Routes
Here's a practical example using React Router.
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { lazy, Suspense } from "react";
const HomePage = lazy(() => import("./pages/home-page"));
const AboutPage = lazy(() => import("./pages/about-page"));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading page...</div>}>
<Routes>
<Route path="/home" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
export default App;
With this setup:
The HomePage component loads only when
/homeis visited.The AboutPage component loads only when
/aboutis visited.The app doesn’t download every route upfront.
Your initial load becomes lighter.
This is especially powerful in apps with multiple pages.
Multiple Suspense Boundaries
With Suspense, you are not limited to wrapping your entire app. You can use multiple Suspense boundaries:
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar />
</Suspense>
<Suspense fallback={<ContentLoader />}>
<MainContent />
</Suspense>
<Suspense fallback={<div>Loading comments... </div>}>
<Comments />
</Suspense>
This allows parts of your UI to load independently.
For example:
Sidebar loads first
The main content loads later
Comments load after that
This creates progressive rendering, which feels extremely smooth to users.
When Should You Use Lazy Loading?
Lazy loading makes sense when you’re building:
Large applications
Dashboard systems
Route-heavy apps
Apps with heavy components (charts, maps, editors)
Feature-based modules
It’s usually unnecessary for very small applications. Over-optimizing a tiny app can actually complicate your architecture.
Important Notes
Suspense does NOT fetch data by default. It only handles component loading (unless using modern data frameworks like React Server Components or specific data libraries).
Lazy loading works only with default exports.
Always wrap lazy components inside
<Suspense>.Error boundaries are important. If a lazy import fails, Suspense alone won’t handle the error, you’ll need an error boundary.
Final Thoughts
Using lazy loading and Suspense was a game changer for me when I first learned it.
At first, it felt like a small optimization, but as my projects grew, especially when working with multiple route-based apps, I realized that they’re not just React features but performance tools. And as frontend developers, performance is a part of user experience.
Thank you so much for reading! ❤️



