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
Structuring the Codebase.
After setting up your codebase, you will proceed to structure the codebase as follows.
- Create a folder named
components
inside thesrc
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
// 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.
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.
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.
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;
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:
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
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
.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.
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!!!
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}
Top comments (1)
Native, built-in
<dialog>
element would be a better option.developer.mozilla.org/en-US/docs/W...