How to create a custom alert component in REACT with Context API
Tags: javascript, react, programming, webdev
Published: 2023-09-10 17:25:01

Do you have to implement a feature that requires you to prompt a user for response (for example the confirmation message before deleting an item)? Or do you want to have a "awesome" designer who designed an awesome component for notifying users on specific actions? Or for the hundred other reasons you have decided to read this article.
In this article, We will be implementing a custom alert component for prompting a user to perform an action or notifying a user using ReactJS with Context API.

## PREREQUISITES
This tutorial makes use of the following languages/technologies and would require you to have a basic knowledge of their usage.
- TypeScript
- ReactJS (bootstrapped with create-react-app)
- CSS

---

## Setting up
Diving into creating the alert component, the first thing you need to do is set up your codebase. Since we are going to be making use of ReactJS you can use any of the React Libraries that are available, but for the purpose of this tutorial, we will be bootstrapping out react application with [`create-react-app`](https://create-react-app.dev/)


## Structuring the Codebase.
After setting up your codebase, you will proceed to structure the codebase as follows.
- Create a folder named `components` inside the `src` folder.
- In the components folder, create a new folder named `Alert`.
- Inside the Alert folder, create a file named `Alert.tsx`.

Copy the following code and paste in your Alert.tsx file

```tsx
// the component UI will live in this file

const Alert = () => {
  return (
    <></>
  );
};

export default Alert;

```

- Next up, you create a file in the Alert folder named `AlertProvider.tsx`.
Inside the AlertProvider.tsx file, paste the following code.

```tsx
import { createContext, useRef, useState } from "react";

export const AlertContext = createContext();

const AlertProvider: React.FC<{ children: React.ReactNode }> = ({
  children,
}) => {
  return (
    <AlertContext.Provider
      value={}
    >
      {children}
    </AlertContext.Provider>
  );
};

export default AlertProvider;

```

Next up, you create a file named `index.tsx` inside the `Alert` folder.
Inside the `index.tsx` file, paste the following code;

Here you are creating a wrapper for the alert context for the alert component and the children to subscribe to the context changes.

```tsx
import { useContext } from "react";
import AlertProvider, { AlertContext } from "./AlertProvider";
import Alert from "./Alert";

// create hook to be used globally to toggle alert component.
export const useAlert = () => useContext(AlertContext);

const AlertContainer: React.FC<{ children: React.ReactNode }> = ({
  children,
}) => {
  return (
    <AlertProvider>
      <Alert />
      {children}
    </AlertProvider>
  );
};

export default AlertContainer;

```

## Register Alert Context in entry file

Now that you have created the context for the alert component, you will register the context globally by updating the entry file. For this tutorial, you will update the `src/index.tsx` file. If you are using other frameworks, your entry file might be different (for NextJS, the entry will would be `/pages/_app.tsx`)

Update your `src/index.tsx` file with the following code.

```tsx
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import AlertContainer from "./components/Alert"; // import the AlertContainer

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
    {/* Wrap your application with the Alert context wrapper you created in `/components/Alert/index.tsx' */}
    <AlertContainer>
      <App />
    </AlertContainer>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

```

## Implement functionality for the Alert component.
Now that you are done setting up, you will now implement the functionalities for your alert component. 
In this tutorial, the major functionalities you will be implementing are 
- `Open alert component`: this will cover the different states in which your component will be rendered. Either as a notifier or as a prompter.
- `Close alert component`: this will cover removing the alert component from user's view and reverting the context state to default.

Now start updating the `src/components/AlertProvider.tsx` file.

First, you need to declare the types that will be used in the functionality.
So your file will look like this;

```tsx
import { createContext, useRef, useState } from "react";

// type for notification object
interface IAlertNotification {
  title: string;
  message: string | React.ReactNode;
  delay?: number;
  autoClose?: boolean;
  inputProps?: React.HTMLAttributes<HTMLInputElement>;
  actions?: {
    text: string;
    callback?: (arg: any) => void;
    props?: React.HTMLAttributes<HTMLButtonElement>;
  }[];
}

// the types for methods and variable available in your alert context
interface IAlertContext {
  alert: (args: IAlertNotification) => void;
  alertShown?: boolean;
  notification?: IAlertNotification;
  close: () => void;
}

// intialise default methods for context.
export const AlertContext = createContext<IAlertContext>({
  alert: () => {},
  close: () => {},
});

const AlertProvider: React.FC<{ children: React.ReactNode }> = ({
  children,
}) => {
  return (
    <AlertContext.Provider
      value={}
    >
      {children}
    </AlertContext.Provider>
  );
};

export default AlertProvider;

```

Next up, you will create the necessary variables and methods to display and hide the alert component. 
So your file will look like this:
```tsx
import { createContext, useRef, useState } from "react";

// type for notification object
interface IAlertNotification {
  title: string;
  message: string | React.ReactNode;
  delay?: number;
  autoClose?: boolean;

  inputProps?: React.HTMLAttributes<HTMLInputElement>;
  actions?: {
    text: string;
    callback?: (arg: any) => void;
    props?: React.HTMLAttributes<HTMLButtonElement>;
  }[];
}
// the types for methods and variable available in your alert context
interface IAlertContext {
  alert: (args: IAlertNotification) => void;
  alertShown?: boolean;
  notification?: IAlertNotification;
  close: () => void;
}

// initialize default methods for context.
export const AlertContext = createContext<IAlertContext>({
  alert: () => {},
  close: () => {},
});

const AlertProvider: React.FC<{ children: React.ReactNode }> = ({
  children,
}) => {
  const [alertShown, setAlertShown] = useState(false); // toggles the view state of the alert component
  const [notification, setNotification] = useState<
    IAlertNotification | undefined
  >(); // stores the configuration data for the alert component
  const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(); // stores the timer value for autoclosing the alert component

  // closes the alert component and reverts all config to default values.
  const close = () => {
    setAlertShown(false);
    setNotification(undefined);
    clearTimeout(timerRef.current);
  };

  // opens the alert component and configures its view state.
  const alert = (args: IAlertNotification) => {
    setNotification(args);
    setAlertShown(true);

    if (args.autoClose) {
      if (timerRef.current) {
        clearTimeout(timerRef.current);
      }

      timerRef.current = setTimeout(() => {
        close();
      }, notification?.delay || 3000);
    }

    return notification;
  };

  return (
    <AlertContext.Provider
      value={{
        notification,
        alert,
        alertShown,
        close,
      }}
    >
      {children}
    </AlertContext.Provider>
  );
};

export default AlertProvider;
```

## Implement Alert component UI.
Now that you have implemented the functionality, the next step is to create the component UI and set up the view states based on the configuration in the context. 

Copy the following code and paste it in the `src/components/Alert/Alert.tsx`

```tsx
import { useState } from "react";
import { useAlert } from ".";
import styles from "./styles.module.css";

const Alert = () => {
  const { notification, alertShown, close } = useAlert();
  const [textInputValue, setTextInputValue] = useState("");

  return (
    <div
      className={`
        ${styles.alert_container} ${alertShown ? styles.show : ''}`}
    >
      <div className={styles.alert}>
        <div className={styles.alert_content}>
          <p className={styles.alert_title}>{notification?.title || ""}</p>

          <div className={styles.alert_body}>
            <p>{notification?.message || ""}</p>
          </div>

          {notification?.inputProps && (
            <div className={styles.alert_textinput}>
              <input
                value={textInputValue}
                onChange={(e) => setTextInputValue(e.target.value)}
                {...notification?.inputProps}
              />
            </div>
          )}

          <div className={styles.alert_action}>
            {(notification?.actions || []).map((action) => (
              <div key={action.text} className={styles.btn}>
                <button
                  {...action.props}
                  onClick={() => {
                    action.callback?.(textInputValue);
                    setTextInputValue("");
                    close();
                  }}
                >
                  {action.text}
                </button>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

export default Alert;
```

Next up, you style the component. 
Create a file named `styles.module.css` in the `src/component/Alert` folder. 

Copy the following code and paste it in the `styles.module.css` file 

```css
.alert_container {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  height: 100%;
  width: 100%;
  z-index: 999;
  transform: translateY(-100%);
  transition: all 500ms;
}
.alert_container.show {
  transform: translateY(0px);
}

.alert {
  width: 100%;
  max-width: 500px;
  margin: 0 auto;
  height: auto;
}

.alert_content {
  background-color: white;
  border-radius: 0px 0px 10px 10px;
  padding: 20px;
  width: 100%;
  box-shadow: 0px 10px 50px rgba(255, 255, 255, 0.3);
}

.alert_title {
  font-weight: 800;
  font-size: 1.275rem;
}

.alert_body {
  margin-top: 10px;
  font-size: 1rem;
}

.alert_textinput {
  margin-top: 20px;
  display: flex;
}

.alert_textinput input {
  width: 100%;
  height: 40px;
  border-radius: 8px;
  border: 1px solid grey;
  padding: 0px 10px;
}

.alert_action {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-top: 20px;
}

.alert_action .btn {
  flex: 1;
}

.alert_action .btn button {
  height: 40px;
  background-color: #282c34;
  border: 0px;
  border-radius: 8px;
  width: 100%;
  font-weight: 600;
  color: white;
  cursor: pointer;
}

```

## Testing the alert component
Now that you have finished implementing the component, you can test the various variations for prompting and notifying the users.

Copy the following code and replace your app.tsx file with it.

```tsx
import logo from "./logo.svg";
import "./App.css";
import { useAlert } from "./components/Alert";

function App() {
  const { alert } = useAlert();

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <button
          onClick={() =>
            alert({
              title: "Notify",
              message: "Notification message",
              autoClose: true,
            })
          }
        >
          Notify
        </button>

        <button
          onClick={() =>
            alert({
              title: "Prompt without feedback",
              message: "Question to ask user before proceeding",
              actions: [
                {
                  text: "Cancel",
                },
                {
                  text: "Confirm",
                  callback: () => {
                    console.log('Confirmed')
                    // action to proceed
                  },
                },
              ],
            })
          }
        >
          Prompt without Feedback
        </button>
        <button
          onClick={() =>
            alert({
              title: "Prompt with feedback",
              message:
                "Question to ask user before proceeding. Expecting message from user",
              inputProps: { placeholder: "Enter feedback" },
              actions: [
                {
                  text: "Cancel",
                },
                {
                  text: "Confirm",
                  callback: (feedback) => {
                    console.log(feedback);
                    // action to proceed
                  },
                },
              ],
            })
          }
        >
          Prompt with Feedback
        </button>
      </header>
    </div>
  );
}

export default App;

```


### Results!!!

![React alert component notification only](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kw2rgoi1tvb0fbtzechf.gif)



![React alert component prompt without feedback](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/imjdxbj1wf91dsed5xwe.gif)


![React alert component prompt with feedback](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p82662w4am4hixlgqpzh.gif)


## Conclusion.
Yayyy, you have successfully implemented a custom alert component in React. I know the article was a bit lengthy, if you have any questions, be sure to leave them in the comments and I will respond. 
I have also taken the liberty to push the code to github. You can access it [here](https://github.com/haywhy43/create-custom-alert-component-with-react}