diff --git a/src/hooks/test/use-warn-on-page-unload-test.js b/src/hooks/test/use-warn-on-page-unload-test.js
new file mode 100644
index 000000000..e68a944d6
--- /dev/null
+++ b/src/hooks/test/use-warn-on-page-unload-test.js
@@ -0,0 +1,62 @@
+import { mount } from 'enzyme';
+import { useState } from 'preact/hooks';
+
+import { useWarnOnPageUnload } from '../use-warn-on-page-unload';
+
+describe('useWarnOnPageUnload', () => {
+ let fakeWindow;
+ const FakeComponent = () => {
+ const [isUnsaved, setUnsaved] = useState(true);
+
+ useWarnOnPageUnload(isUnsaved, fakeWindow);
+
+ return (
+
+ );
+ };
+ const createComponent = () => mount();
+
+ const waitForBeforeUnloadEvent = () => {
+ const promise = new Promise(resolve =>
+ fakeWindow.addEventListener('beforeunload', resolve),
+ );
+ fakeWindow.dispatchEvent(new Event('beforeunload', { cancelable: true }));
+
+ return promise;
+ };
+
+ beforeEach(() => {
+ fakeWindow = new EventTarget();
+ });
+
+ it('registers event listener when unsaved data is true', async () => {
+ createComponent();
+
+ const event = await waitForBeforeUnloadEvent();
+
+ assert.isTrue(event.defaultPrevented);
+ assert.equal(event.returnValue, '');
+ });
+
+ it('unregisters event listener when unsaved data is false', async () => {
+ const wrapper = createComponent();
+
+ wrapper.find('button').simulate('click');
+ wrapper.update();
+
+ const event = await waitForBeforeUnloadEvent();
+
+ assert.isFalse(event.defaultPrevented);
+ });
+
+ it('unregisters event listener when component is unmounted', async () => {
+ const wrapper = createComponent();
+ wrapper.unmount();
+
+ const event = await waitForBeforeUnloadEvent();
+
+ assert.isFalse(event.defaultPrevented);
+ });
+});
diff --git a/src/hooks/use-warn-on-page-unload.ts b/src/hooks/use-warn-on-page-unload.ts
new file mode 100644
index 000000000..75c999695
--- /dev/null
+++ b/src/hooks/use-warn-on-page-unload.ts
@@ -0,0 +1,29 @@
+import { useEffect } from 'preact/hooks';
+
+const noop = () => {};
+
+/**
+ * Registers an event listener to window's 'beforeunload' if `hasUnsavedData` is true.
+ * It also unregisters the event if `hasUnsavedData` is false or the component is unmounted.
+ *
+ * This event listener makes the browser warn the user about potential unsaved changes,
+ * and gives the user the opportunity to cancel the page unload if desired.
+ *
+ * @link https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
+ */
+export function useWarnOnPageUnload(hasUnsavedData: boolean, window_ = window) {
+ useEffect(() => {
+ if (!hasUnsavedData) {
+ return noop;
+ }
+
+ const listener = (e: BeforeUnloadEvent) => {
+ e.preventDefault();
+ e.returnValue = '';
+ };
+
+ window_.addEventListener('beforeunload', listener);
+
+ return () => window_.removeEventListener('beforeunload', listener);
+ }, [hasUnsavedData, window_]);
+}
diff --git a/src/index.ts b/src/index.ts
index e0f8830ac..94fe39b9c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -9,6 +9,7 @@ export { useStableCallback } from './hooks/use-stable-callback';
export { useSyncedRef } from './hooks/use-synced-ref';
export { useToastMessages } from './hooks/use-toast-messages';
export { useValidateOnSubmit } from './hooks/use-validate-on-submit';
+export { useWarnOnPageUnload } from './hooks/use-warn-on-page-unload';
export type {
ToastMessagesState,
ToastMessageData,