diff --git a/package-lock.json b/package-lock.json
index 78fb28e2e1f89d63d41d5285d30e762c36e8f5b1..57dc9ec5ab190f3d487fe1a33a0f6a3817e2929a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -49710,17 +49710,17 @@
         "@sikt/sds-button": "^4.0.1",
         "@sikt/sds-core": "^4.2.0",
         "@sikt/sds-form": "^3.0.1",
+        "@sikt/sds-hooks": "^0.1.0",
         "@sikt/sds-icons": "^2.0.2",
         "@sikt/sds-input": "^4.0.2",
         "react-aria-components": "^1.5.0"
       },
       "peerDependencies": {
-        "@types/react": "^18.0.0",
-        "@types/react-dom": "^18.0.0",
+        "@types/react": "^18.0.0 || ^19.0.0",
+        "@types/react-dom": "^18.0.0 || ^19.0.0",
         "clsx": "^2.1.0",
-        "react": "^18.0.0",
-        "react-dom": "^18.0.0",
-        "usehooks-ts": "^3.1.0"
+        "react": "^18.0.0 || ^19.0.0",
+        "react-dom": "^18.0.0 || ^19.0.0"
       }
     },
     "packages/input-datepicker/node_modules/@sikt/sds-core/node_modules/@sikt/sds-tokens": {
diff --git a/packages/dialog/src/Dialog.test.tsx b/packages/dialog/src/Dialog.test.tsx
index 170493dec31f41dc5c68c1e3abe4b386f628c3e3..42e2c44c9a2d51ee989b5147b84932e6bfebede5 100644
--- a/packages/dialog/src/Dialog.test.tsx
+++ b/packages/dialog/src/Dialog.test.tsx
@@ -98,13 +98,13 @@ describe("Dialog", () => {
       const user = userEvent.setup();
       const handleClose = jest.fn();
       const headingText = "Test Heading";
-      const closeButtonText = "Close";
+      const closeButtonText = "Close ARIA";
       render(
         <Dialog
           open
           onClose={handleClose}
           heading={headingText}
-          closeButtonLabel={closeButtonText}
+          closeButtonAriaLabel={closeButtonText}
           footer={[<button key="primary">Click me!</button>]}
           dismissable
         >
@@ -125,7 +125,9 @@ describe("Dialog", () => {
       expect(footerButton).toBeInTheDocument();
 
       // Check if the close button is rendered correctly
-      const closeButton = screen.getByRole("button", { name: closeButtonText });
+      const closeButton = screen.getByRole("button", {
+        name: closeButtonText,
+      });
       expect(closeButton).toBeInTheDocument();
 
       await user.click(closeButton);
@@ -156,7 +158,6 @@ describe("Dialog", () => {
       rerender(
         <Dialog
           closeButtonLabel={closeButtonText}
-          dismissable
           open
           onClose={handleClose}
           heading="Test heading"
@@ -244,4 +245,26 @@ describe("Dialog", () => {
     await user.keyboard("[Escape]");
     expect(handleClose).toHaveBeenCalledTimes(0);
   });
+
+  it("closes dialog when click outside wrapper", async () => {
+    const user = userEvent.setup();
+    const handleClose = jest.fn();
+    render(
+      <Dialog
+        footer={[<button key="primary">Click me!</button>]}
+        open
+        onClose={handleClose}
+        heading="Test heading"
+        dismissable
+        closeButtonLabel="Close dialog"
+        data-testid="test"
+      >
+        <p>Dialog Content</p>
+      </Dialog>,
+    );
+
+    await user.click(screen.getByTestId("test"));
+
+    expect(handleClose).toHaveBeenCalledTimes(1);
+  });
 });
diff --git a/packages/dialog/src/Dialog.tsx b/packages/dialog/src/Dialog.tsx
index 3f96d00c7098770cfdcaa02c0f15fb05186669b6..725ed8311b945984ceeff20559a7f00d4f494f05 100644
--- a/packages/dialog/src/Dialog.tsx
+++ b/packages/dialog/src/Dialog.tsx
@@ -1,6 +1,6 @@
 import { Button } from "@sikt/sds-button";
 import { Heading1, Paragraph } from "@sikt/sds-core";
-import { useWindowResize } from "@sikt/sds-hooks";
+import { useClickOutside, useKeydown, useWindowResize } from "@sikt/sds-hooks";
 import { XIcon } from "@sikt/sds-icons";
 import { clsx } from "clsx/lite";
 import {
@@ -42,12 +42,12 @@ export type DialogProps = DialogBaseProps &
         closeButtonAriaLabel?: never;
       }
     | {
-        dismissable: true;
+        dismissable?: true;
         closeButtonLabel: string;
         closeButtonAriaLabel?: never;
       }
     | {
-        dismissable: true;
+        dismissable?: true;
         closeButtonLabel?: never;
         closeButtonAriaLabel: string;
       }
@@ -65,34 +65,23 @@ export const Dialog = ({
   onClose,
   open,
   subheading,
+  ...rest
 }: DialogProps) => {
   const dialogRef = useRef<HTMLDialogElement>(null);
+  const wrapperRef = useRef<HTMLDivElement>(null);
   const contentRef = useRef<HTMLDivElement>(null);
   const [isScrolling, setIsScrolling] = useState<boolean>(false);
+  const [isOpen, setIsOpen] = useState<boolean>(open);
 
-  const handleBackdropClick = (event: MouseEvent) => {
-    if (dismissable) {
-      const bounds = (
-        event.target as HTMLDialogElement
-      ).getBoundingClientRect();
-      if (
-        bounds.left > event.clientX ||
-        bounds.right < event.clientX ||
-        bounds.top > event.clientY ||
-        bounds.bottom < event.clientY
-      )
-        onClose();
+  const handleBackdropClick = () => {
+    if (dismissable && isOpen) {
+      onClose();
     }
   };
 
-  const handleEscapeKey = (event: KeyboardEvent) => {
-    if (event.key === "Escape") {
-      if (dismissable) {
-        onClose();
-      } else {
-        event.preventDefault();
-        event.stopPropagation();
-      }
+  const handleEscapeKey = () => {
+    if (dismissable && isOpen) {
+      onClose();
     }
   };
 
@@ -100,23 +89,15 @@ export const Dialog = ({
     if (dialogRef.current) {
       if (open) {
         dialogRef.current.showModal();
-        dialogRef.current.addEventListener("click", handleBackdropClick);
-        document.addEventListener("keydown", handleEscapeKey);
-        document.body.style.overflow = "hidden";
       } else {
         dialogRef.current.close();
-        dialogRef.current.removeEventListener("click", handleBackdropClick);
-        document.removeEventListener("keydown", handleEscapeKey);
-        document.body.style.overflow = "unset";
       }
     }
 
-    return () => {
-      if (dialogRef.current) {
-        dialogRef.current.removeEventListener("click", handleBackdropClick);
-      }
+    setIsOpen(open);
+    document.body.style.overflow = open ? "hidden" : "unset";
 
-      document.removeEventListener("keydown", handleEscapeKey);
+    return () => {
       document.body.style.overflow = "unset";
     };
   }, [open]);
@@ -133,6 +114,8 @@ export const Dialog = ({
   const headingId = `${id}-heading`;
   const contentId = `${id}-content`;
 
+  useClickOutside(wrapperRef, handleBackdropClick);
+  useKeydown(null, "Escape", handleEscapeKey);
   useWindowResize(checkScroll, { throttleTime: 200 });
   useEffect(checkScroll, [children]);
 
@@ -147,40 +130,43 @@ export const Dialog = ({
       aria-describedby={contentLabel ? undefined : contentId}
       aria-label={contentLabel}
       ref={dialogRef}
+      {...rest}
     >
-      <header className="sds-dialog__header">
-        <div
-          id={headingId}
-          data-testid="headings"
-          className="sds-dialog__heading"
-        >
-          <Heading1 variant="medium">{heading}</Heading1>
-          {subheading !== undefined && <Paragraph>{subheading}</Paragraph>}
-        </div>
+      <div ref={wrapperRef}>
+        <header className="sds-dialog__header">
+          <div
+            id={headingId}
+            data-testid="headings"
+            className="sds-dialog__heading"
+          >
+            <Heading1 variant="medium">{heading}</Heading1>
+            {subheading !== undefined && <Paragraph>{subheading}</Paragraph>}
+          </div>
 
-        {dismissable && (
-          <Button
-            variant="transparent"
-            icon={<XIcon />}
-            className="sds-dialog__close-button"
-            onClick={onClose}
-            aria-label={closeButtonLabel ? undefined : closeButtonAriaLabel}
+          {dismissable && (
+            <Button
+              variant="transparent"
+              icon={<XIcon />}
+              className="sds-dialog__close-button"
+              onClick={onClose}
+              aria-label={closeButtonLabel ? undefined : closeButtonAriaLabel}
+            >
+              {closeButtonLabel}
+            </Button>
+          )}
+        </header>
+        <div className="sds-dialog__content-wrapper" ref={contentRef}>
+          <div
+            id={contentId}
+            data-testid="content"
+            className="sds-dialog__content"
           >
-            {closeButtonLabel}
-          </Button>
-        )}
-      </header>
-      <div className="sds-dialog__content-wrapper" ref={contentRef}>
-        <div
-          id={contentId}
-          data-testid="content"
-          className="sds-dialog__content"
-        >
-          {children}
+            {children}
+          </div>
+          {footer !== undefined && (
+            <div className="sds-dialog__footer">{footer}</div>
+          )}
         </div>
-        {footer !== undefined && (
-          <div className="sds-dialog__footer">{footer}</div>
-        )}
       </div>
     </dialog>
   );
diff --git a/packages/hooks/index.ts b/packages/hooks/index.ts
index f65bcd4b7ba757834aca5d369d934ac81e886a30..08e9f763ec02ee1075e4b01999f9d2b70f18d923 100644
--- a/packages/hooks/index.ts
+++ b/packages/hooks/index.ts
@@ -1,3 +1,5 @@
+export { useClickOutside } from "./src/useClickOutside/useClickOutside";
+export { useKeydown } from "./src/useKeydown/useKeydown";
 export {
   useWindowResize,
   type useWindowResizeOptions,
diff --git a/packages/hooks/src/useClickOutside/useClickOutside.test.tsx b/packages/hooks/src/useClickOutside/useClickOutside.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..7ff9bce2b04f4da330c74a1f7027249e6093fe03
--- /dev/null
+++ b/packages/hooks/src/useClickOutside/useClickOutside.test.tsx
@@ -0,0 +1,37 @@
+import { renderHook } from "@testing-library/react";
+import { userEvent } from "@testing-library/user-event";
+import { useClickOutside } from "./useClickOutside";
+
+describe("useClickOutside", () => {
+  it("should call callback on click outside", async () => {
+    const user = userEvent.setup();
+    const ref = { current: document.createElement("div") };
+    const callback = jest.fn();
+
+    renderHook(() => {
+      useClickOutside(ref, callback);
+    });
+
+    expect(callback).not.toHaveBeenCalled();
+
+    await user.click(document.body);
+
+    expect(callback).toHaveBeenCalledTimes(1);
+  });
+
+  it("should not call callback on click inside", async () => {
+    const user = userEvent.setup();
+    const ref = { current: document.createElement("div") };
+    const callback = jest.fn();
+
+    renderHook(() => {
+      useClickOutside(ref, callback);
+    });
+
+    expect(callback).not.toHaveBeenCalled();
+
+    await user.click(ref.current);
+
+    expect(callback).not.toHaveBeenCalled();
+  });
+});
diff --git a/packages/hooks/src/useClickOutside/useClickOutside.tsx b/packages/hooks/src/useClickOutside/useClickOutside.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..c900690760dc631b4c38ac2a763a314da6a6836e
--- /dev/null
+++ b/packages/hooks/src/useClickOutside/useClickOutside.tsx
@@ -0,0 +1,20 @@
+import { RefObject, useEffect } from "react";
+
+export const useClickOutside = (
+  ref: RefObject<HTMLElement>,
+  callback: (event: MouseEvent) => void,
+) => {
+  useEffect(() => {
+    const listener = (event: MouseEvent) => {
+      if (ref.current && !ref.current.contains(event.target as Node)) {
+        callback(event);
+      }
+    };
+
+    document.addEventListener("click", listener);
+
+    return () => {
+      document.removeEventListener("click", listener);
+    };
+  }, [ref, callback]);
+};
diff --git a/packages/hooks/src/useKeydown/useKeydown.test.tsx b/packages/hooks/src/useKeydown/useKeydown.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fd69edefff23a244603492e2e569415fef99d563
--- /dev/null
+++ b/packages/hooks/src/useKeydown/useKeydown.test.tsx
@@ -0,0 +1,50 @@
+import { fireEvent, renderHook } from "@testing-library/react";
+import { userEvent } from "@testing-library/user-event";
+import { useKeydown } from "./useKeydown";
+
+describe("useKeydown", () => {
+  it("should call callback on keydown with correct key", async () => {
+    const ref = { current: document.createElement("button") };
+    const callback = jest.fn();
+
+    renderHook(() => {
+      useKeydown(ref, "Escape", callback);
+    });
+
+    expect(callback).not.toHaveBeenCalled();
+
+    fireEvent.keyDown(ref.current, { key: "Escape" });
+
+    expect(callback).toHaveBeenCalledWith(expect.any(KeyboardEvent));
+  });
+
+  it("should not call callback on keydown with incorrect key", async () => {
+    const ref = { current: document.createElement("div") };
+    const callback = jest.fn();
+
+    renderHook(() => {
+      useKeydown(ref, "Escape", callback);
+    });
+
+    expect(callback).not.toHaveBeenCalled();
+
+    fireEvent.keyDown(ref.current, { key: "Enter" });
+
+    expect(callback).not.toHaveBeenCalled();
+  });
+
+  it("should call callback on document keydown with correct key", async () => {
+    const user = userEvent.setup();
+    const callback = jest.fn();
+
+    renderHook(() => {
+      useKeydown(null, "Escape", callback);
+    });
+
+    expect(callback).not.toHaveBeenCalled();
+
+    await user.keyboard("{Escape}");
+
+    expect(callback).toHaveBeenCalledWith(expect.any(KeyboardEvent));
+  });
+});
diff --git a/packages/hooks/src/useKeydown/useKeydown.tsx b/packages/hooks/src/useKeydown/useKeydown.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2bcf1632bd49ed1f150a34b66cd6a728ba3d6f53
--- /dev/null
+++ b/packages/hooks/src/useKeydown/useKeydown.tsx
@@ -0,0 +1,26 @@
+import { RefObject, useEffect } from "react";
+
+export const useKeydown = (
+  ref: RefObject<HTMLElement> | null,
+  key: string,
+  callback: (event: KeyboardEvent) => void,
+) => {
+  useEffect(() => {
+    const targetElement = ref?.current ?? document;
+
+    const listener = (event: KeyboardEvent) => {
+      if (event.key === key) {
+        event.preventDefault();
+        callback(event);
+      }
+    };
+
+    // @ts-expect-error: Type '(event: KeyboardEvent) => void' is not assignable to type 'EventListener'.
+    targetElement.addEventListener("keydown", listener);
+
+    return () => {
+      // @ts-expect-error: Type '(event: KeyboardEvent) => void' is not assignable to type 'EventListener'.
+      targetElement.removeEventListener("keydown", listener);
+    };
+  }, [ref, key, callback]);
+};
diff --git a/packages/input-datepicker/package.json b/packages/input-datepicker/package.json
index e92fc4a13fcc3362ecba22acc1aa1d55662cb4dc..2f2d6418c0af3b31c0681a65cd72b7a6f8de9a66 100644
--- a/packages/input-datepicker/package.json
+++ b/packages/input-datepicker/package.json
@@ -38,16 +38,16 @@
     "@sikt/sds-button": "^4.0.1",
     "@sikt/sds-core": "^4.2.0",
     "@sikt/sds-form": "^3.0.1",
+    "@sikt/sds-hooks": "^0.1.0",
     "@sikt/sds-icons": "^2.0.2",
     "@sikt/sds-input": "^4.0.2",
     "react-aria-components": "^1.5.0"
   },
   "peerDependencies": {
-    "@types/react": "^18.0.0",
-    "@types/react-dom": "^18.0.0",
+    "@types/react": "^18.0.0 || ^19.0.0",
+    "@types/react-dom": "^18.0.0 || ^19.0.0",
     "clsx": "^2.1.0",
-    "react": "^18.0.0",
-    "react-dom": "^18.0.0",
-    "usehooks-ts": "^3.1.0"
+    "react": "^18.0.0 || ^19.0.0",
+    "react-dom": "^18.0.0 || ^19.0.0"
   }
 }
diff --git a/packages/input-datepicker/src/InputDatepicker.tsx b/packages/input-datepicker/src/InputDatepicker.tsx
index 9465baf01409518850a4a9349a2d17c0f6c0cd58..8b28a74c308aea9d93e0e86802864096ce1df039 100644
--- a/packages/input-datepicker/src/InputDatepicker.tsx
+++ b/packages/input-datepicker/src/InputDatepicker.tsx
@@ -1,6 +1,7 @@
 import { I18nProvider } from "@react-aria/i18n";
 import { Button, ButtonProps } from "@sikt/sds-button";
 import { HelpText, Label } from "@sikt/sds-form";
+import { useClickOutside, useKeydown } from "@sikt/sds-hooks";
 import {
   CalendarBlankIcon,
   CaretLeftIcon,
@@ -15,7 +16,8 @@ import {
   useRef,
   useState,
   useContext,
-  MouseEvent,
+  MouseEvent as ReactMouseEvent,
+  KeyboardEvent as ReactKeyboardEvent,
 } from "react";
 import {
   Calendar,
@@ -33,7 +35,6 @@ import {
   Text,
   DatePickerStateContext,
 } from "react-aria-components";
-import { useEventListener, useOnClickOutside } from "usehooks-ts";
 import "./input-datepicker.pcss";
 
 interface InputDatepickerBaseProps
@@ -67,7 +68,7 @@ export type InputDatepickerProps = InputDatepickerBaseProps &
 const DatepickerClearButton = (clearActionProps?: ClearActionProps) => {
   const state = useContext(DatePickerStateContext);
 
-  const handleClearClick = (event: MouseEvent<HTMLButtonElement>) => {
+  const handleClearClick = (event: ReactMouseEvent<HTMLButtonElement>) => {
     if (clearActionProps?.onClick) {
       clearActionProps.onClick(event);
     }
@@ -75,11 +76,9 @@ const DatepickerClearButton = (clearActionProps?: ClearActionProps) => {
     state && state.setValue(null);
   };
 
-  const handleClearKeyDown = (
-    event: React.KeyboardEvent<HTMLButtonElement>,
-  ) => {
+  const handleClearKeydown = (event: ReactKeyboardEvent<HTMLButtonElement>) => {
     if (event.key === " " || event.key === "Enter") {
-      handleClearClick(event as unknown as MouseEvent<HTMLButtonElement>);
+      handleClearClick(event as unknown as ReactMouseEvent<HTMLButtonElement>);
     }
   };
 
@@ -91,7 +90,7 @@ const DatepickerClearButton = (clearActionProps?: ClearActionProps) => {
         iconVariant="only"
         className="sds-input__clear"
         onClick={handleClearClick}
-        onKeyUp={handleClearKeyDown}
+        onKeyDown={handleClearKeydown}
         icon={<XIcon />}
         aria-label={clearActionProps?.["aria-label"] ?? "Tøm datofelt"}
         type={clearActionProps?.type ?? "button"}
@@ -124,22 +123,17 @@ export const InputDatepicker = forwardRef<HTMLDivElement, InputDatepickerProps>(
     const helpTextId = `${id}-help-text`;
     const [calendarOpen, setCalendarOpen] = useState(false);
 
-    const onEscapeKey = (event: KeyboardEvent) => {
-      if (event.key === "Escape") {
-        if ("current" in calendarRef && calendarRef.current) {
-          setCalendarOpen(false);
-          (inputRef.current?.firstChild as HTMLElement).focus();
-        }
-      }
+    const handleEscapeKeydown = () => {
+      setCalendarOpen(false);
+      (inputRef.current?.firstChild as HTMLElement).focus();
     };
 
-    useEventListener("keyup", onEscapeKey, calendarRef);
-
     const handleClickOutside = () => {
       setCalendarOpen(false);
     };
 
-    useOnClickOutside(calendarRef, handleClickOutside);
+    useClickOutside(calendarRef, handleClickOutside);
+    useKeydown(calendarRef, "Escape", handleEscapeKeydown);
 
     return (
       <I18nProvider locale={lang}>
@@ -191,7 +185,7 @@ export const InputDatepicker = forwardRef<HTMLDivElement, InputDatepickerProps>(
               onClick={() => {
                 setCalendarOpen(!calendarOpen);
               }}
-              onKeyUp={(event) => {
+              onKeyDown={(event) => {
                 if (event.key === " " || event.key === "Enter") {
                   setCalendarOpen(!calendarOpen);
                 }