Creating Global Lodaers and Alerts in React App

The 2 most common types of UI presentations to be shown are loaders and alerts/notifications. So it makes sense to add these 2 as global components and then trigger then as and when required. They usually have the same UI throughout the app except for minor variantions like type of alert (error, warning etc..). So we can just pass some props to these global components and reducde repeating the same code over and over.

To achieve this we are going to use React's Context API. We are also using Material UI as our design library to help us with the UI for Alerts and Loaders.

First let's create our Loader component

import React from 'react';
import Backdrop from '@mui/material/Backdrop';
import CircularProgress from '@mui/material/CircularProgress';

export default function Loaders() {
  return (
    <Backdrop
      sx={{
        color: '#fffe',
        zIndex: (theme) => theme.zIndex.drawer + 1,
        width: '100%',
        display: 'flex',
        flexFlow: 'column',
        gap: '3rem'
      }}
      open={true}
    >
      'Loading...'
      <CircularProgress color="primary" />
    </Backdrop>
  );
}

Now we are gonna add this to our App.tsx which has the BrowserRouter along with the routes for all the other pages of the app. This will just show a full page Circular Progress Loader always when invoked.

function App() {
  return (
      <ThemeProvider theme={setTheme(fallbackPartnerObject)}>
        <div className="App">
          <Loaders />
          <BrowserRouter> ..... </BrowserRouter>
        </div>
      </ThemeProvider >
  )
}

Now we have to modify this so we can control the visibillity of the loader from anywhere in our app. For that we make use of React's Context API. I have a seprate file called Store.tsx that holds all my contexts. We can also add the Context directly in our index.tsx file and wrap our app inside it.

index.tsx

ReactDOM.render(
  <React.StrictMode>
    <Store>
      <App />
    </Store>
  </React.StrictMode>,
  document.getElementById('root')
);

Store.tsx

import React, { createContext, useState, useMemo } from 'react';

interface LoaderProps {
  open: boolean;
  message?: string;
}

const defaultLoaderProps: LoaderProps = {
  open: false,
  message: 'Loading...'
};

export const LoaderContext = createContext({
  loaderProps: defaultLoaderProps,
  setLoaderProps: (props: LoaderProps) => {
    return;
  }
});

const Store = ({ children }: any) => {
  const [loaderProps, setLoaderProps] = useState(defaultLoaderProps);

  return (
    <LoaderContext.Provider
      value={useMemo(() => ({ loaderProps, setLoaderProps }), [loaderProps])}
    >
      {children}
    </LoaderContext.Provider>
  );
};

export default Store;

Our LoaderContext has a setLoaderProps method that allows us to toggle the visibility of the loader in addtion to setting a custom message.

Now let's modify our Loader components to use the props of LoaderContext.

import React, { useContext } from 'react';
import { LoaderContext } from 'Store';

import Backdrop from '@mui/material/Backdrop';
import CircularProgress from '@mui/material/CircularProgress';

export default function Loaders() {
const { loaderProps, setLoaderProps } = useContext(LoaderContext);

  return (
    <Backdrop
      sx={{
        color: '#fffe',
        zIndex: (theme) => theme.zIndex.drawer + 1,
        width: '100%',
        display: 'flex',
        flexFlow: 'column',
        gap: '3rem'
      }}
     open={loaderProps.open}
  >
      {loaderProps.message || 'Loading...'}
      <CircularProgress color="primary" />
    </Backdrop>
  );
}

Our Loader will now show up only when we set the open property of LoaderContext to true. Let's do that in one of our views.

import { LoaderContext } from 'Store';
..
...
....

const HomePage: FC = () => {
  const { setLoaderProps } = useContext(LoaderContext);
  ...
  .....
  setLoaderProps({
      open: true
   });

And that's it! We have just triggered the Loader from a View by just passing the right props. We can use the same approach to trigger Alerts/Toast Notifications too.