In web applications, it’s very frustrating for users to lose their progress due to an accidental page refresh or navigation. As developers, we can implement a simple yet powerful mechanism to prevent such data loss. In this tutorial, I’ll show you how to create an exit prevention system for forms in React that keeps your users’ data safe.

Detect form changes
The first step is to detect if the form is touched or not. For this purpose:
- I’ll add a state variable
isDirty
- attach an
input
event listener to theform
element in anuseEffect
- If there is any input, I’ll set
isDirty
totrue
function Form() {
const formRef = useRef(null)
const [isDirty, setIsDirty] = useState(false);
useEffect(() => {
const setDirtyState = () => {
setIsDirty(true);
}
formRef?.current?.addEventListener('input', setDirtyState);
return () => {
formRef?.current?.removeEventListener('input', setDirtyState);
}
}, [])
return (
<form ref={formRef} className="form">
<div>
<label htmlFor="name">Name</label>
<input type="text" id="name" />
</div>
<div>
<label htmlFor="email">Email </label>
<input type="email" id="email" />
</div>
<div>
<label htmlFor="pass">Password </label>
<input type="password" id="pass" />
</div>
</form>
)
Prevent the user from leaving the page if there are unsaved changes
The next step is to implement an exit prevention mechanism using the beforeunload
event. This approach allows us to detect when a user attempts to leave the page and trigger a warning dialog if there are unsaved changes. Here’s how we can intercept and manage page exit attempts in React:
useEffect(() => {
// No need to prevent the user from leaving the page, if there are no changes
if (!isDirty) {
return;
}
const preventLeaving = (e) => {
e.preventDefault();
}
window.addEventListener('beforeunload', preventLeaving);
// Clean up the event listener to avoid memory leaks
return () => {
window.removeEventListener('beforeunload', preventLeaving);
}
}, [isDirty])
Now you can see that whenever the user tries to leave the page with unsaved changes, this prompt appears:

Here is the final code:
import { useEffect, useRef, useState } from 'react'
import './Form.css'
function Form() {
const formRef = useRef(null)
const [isDirty, setIsDirty] = useState(false);
useEffect(() => {
const setDirtyState = () => {
setIsDirty(true);
}
formRef?.current?.addEventListener('input', setDirtyState);
return () => {
formRef?.current?.removeEventListener('input', setDirtyState);
}
}, [])
useEffect(() => {
// No need to prevent the user from leaving the page, if there are no changes
if (!isDirty) {
return;
}
const preventLeaving = (e) => {
e.preventDefault();
}
window.addEventListener('beforeunload', preventLeaving);
// Clean up the event listener to avoid memory leaks
return () => {
window.removeEventListener('beforeunload', preventLeaving);
}
}, [isDirty])
return (
<form ref={formRef} className="form">
<div>
<label htmlFor="name">Name</label>
<input type="text" id="name" />
</div>
<div>
<label htmlFor="email">Email </label>
<input type="email" id="email" />
</div>
<div>
<label htmlFor="pass">Password </label>
<input type="password" id="pass" />
</div>
</form>
)
}
export default Form
Enjoy!