Back to Blog
Lesson 27 of the Intermediate React: Hooks, State & Data Patterns course
ReactUI DesignJune 26, 20263 min read

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.

ReactUIUXError HandlingLoading Statejavascriptfrontend

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.

JSX
import 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.

JSX
const 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.

JSX
const 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:

  1. Create an ErrorBoundary.jsx file and wrap your main route content.
  2. Build a SkeletonLoader component that matches the dimensions of your existing dashboard cards.
  3. Update your useQuery calls to conditionally render the SkeletonLoader when isLoading is 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 componentDidCatch method. If you just return null, 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.

Similar Posts