Error Handling and Loading UI: Building Resilient React Dashboards
Master error handling and loading UI in React. Learn to build resilient error boundaries and reusable skeletons for a polished, professional user experience.
Previously in this course, we explored integrating live data into the dashboard with react query. While fetching data efficiently is critical, a truly professional application must remain stable when things go wrong. Today, we’re leveling up our UX by implementing global error boundaries and reusable loading skeletons.
The Philosophy of Resilient UI
In production, your API will eventually fail, and your network will occasionally crawl. If your app simply crashes or shows a blank screen, you lose user trust. Robust error handling and thoughtful loading state management are not "extra" features; they are core requirements for a professional UI.
We want to move away from generic "Loading..." text and instead use visual placeholders that mimic the final layout. This reduces perceived wait times and keeps the user engaged. Simultaneously, we need to catch unexpected runtime errors before they unmount our entire component tree.
Implementing Error Boundaries
Standard try/catch blocks don't work for errors inside the React component lifecycle. For that, we use Error Boundaries—React components that catch JavaScript errors anywhere in their child component tree.
JSXimport React from CE9178">'react'; class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, errorInfo) { console.error("Dashboard Error:", error, errorInfo); } render() { if (this.state.hasError) { return ( <div className="error-fallback"> <h2>Something went wrong.</h2> <button onClick={() => window.location.reload()}>Retry</button> </div> ); } return this.props.children; } }
By wrapping our main dashboard components in this boundary, we ensure that a single failing widget doesn't crash the entire user session.
Creating Reusable Loading Skeletons
Instead of spinners, use skeletons to maintain the layout's structural integrity. Create a base component that provides the "shimmer" effect, then compose it into your dashboard modules.
JSXconst Skeleton = ({ width = "100%", height = "20px" }) => ( <div className="skeleton-pulse" style={{ width, height, borderRadius: CE9178">'4px', background: CE9178">'#e0e0e0' }} /> ); // Usage in a Dashboard Card const MetricSkeleton = () => ( <div className="card"> <Skeleton height="30px" width="60%" /> <Skeleton height="15px" width="40%" /> </div> );
Handling API Failures
When using React Query, you already have access to isError and isLoading states. Combine these with your UI components to provide feedback. As discussed in handling asynchronous state in react for wordpress plugins, it is vital to keep your state transitions predictable.
JSXconst DashboardWidget = () => { const { data, isLoading, isError } = useQuery({ queryKey: [CE9178">'metrics'], queryFn: fetchMetrics }); if (isLoading) return <MetricSkeleton />; if (isError) return <ErrorMessage message="Failed to load metrics." />; return <div>{data.value}</div>; };
Hands-on Exercise: The "Resilient Dashboard"
Your task is to integrate these patterns into our running project:
- Create an
ErrorBoundary.jsxfile and wrap your main route content. - Build a
SkeletonLoadercomponent that matches the dimensions of your existing dashboard cards. - Update your
useQuerycalls to conditionally render theSkeletonLoaderwhenisLoadingis true.
Common Pitfalls
- Over-wrapping: Don't put an Error Boundary around every single button or small component. It makes debugging harder and adds unnecessary overhead. Use them at the "page" or "major module" level.
- Swallowing Errors: Always log errors to a service like Sentry or LogRocket in your
componentDidCatchmethod. If you just returnnull, you'll never know why your users are seeing your fallback UI. - Inconsistent Skeletons: Ensure your skeleton dimensions closely match the real content. If the layout shifts significantly when the data loads, the user experience will feel jarring.
Recap
We’ve learned that error handling is about containment, while loading state management is about visual continuity. By utilizing React Error Boundaries and tailored skeleton loaders, you ensure your dashboard remains functional and polished, even when the underlying data layer experiences hiccups. These techniques bridge the gap between a prototype and a production-grade application.
Up next, we will explore controlled vs uncontrolled components to refine how we handle user input within our dashboard forms.
Work with me

Headless WordPress + Next.js Frontend Development
Keep WordPress for content, get a lightning-fast Next.js frontend. The best of both worlds — familiar editing, modern speed.

React & Next.js Dashboard / Admin UI Development
A clean, data-rich dashboard UI in React or Next.js — charts, tables, and real-time data that your users will actually enjoy using.