diff --git a/package.json b/package.json
index d1b8aecc..add69b46 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
     "node-forge": "^1.3.1",
     "ofetch": "^1.0.0",
     "react": "^17.0.2",
+    "type-fest": "^4.3.3",
     "react-dom": "^17.0.2",
     "react-ga4": "^2.0.0",
     "react-google-recaptcha-v3": "^1.10.1",
@@ -103,7 +104,6 @@
     "tailwind-scrollbar": "^2.0.1",
     "tailwindcss": "^3.2.4",
     "tailwindcss-themer": "^3.1.0",
-    "type-fest": "^4.3.3",
     "typescript": "^4.6.4",
     "vite": "^4.4.12",
     "vite-plugin-checker": "^0.5.6",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ffe23820..924ee5d0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -110,6 +110,9 @@ dependencies:
   subsrt-ts:
     specifier: ^2.1.1
     version: 2.1.1
+  type-fest:
+    specifier: ^4.3.3
+    version: 4.3.3
   zustand:
     specifier: ^4.3.9
     version: 4.4.1(@types/react@17.0.65)(immer@10.0.2)(react@17.0.2)
@@ -238,9 +241,6 @@ devDependencies:
   tailwindcss-themer:
     specifier: ^3.1.0
     version: 3.1.0(tailwindcss@3.3.3)
-  type-fest:
-    specifier: ^4.3.3
-    version: 4.3.3
   typescript:
     specifier: ^4.6.4
     version: 4.9.5
@@ -6056,7 +6056,7 @@ packages:
   /type-fest@4.3.3:
     resolution: {integrity: sha512-bxhiFii6BBv6UiSDq7uKTMyADT9unXEl3ydGefndVLxFeB44LRbT4K7OJGDYSyDrKnklCC1Pre68qT2wbUl2Aw==}
     engines: {node: '>=16'}
-    dev: true
+    dev: false
 
   /typed-array-buffer@1.0.0:
     resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
index ec4951a6..0f80d7a0 100644
--- a/src/components/layout/Footer.tsx
+++ b/src/components/layout/Footer.tsx
@@ -1,5 +1,7 @@
+import { useCallback } from "react";
 import { useTranslation } from "react-i18next";
 import { useHistory } from "react-router-dom";
+import { RequireExactlyOne } from "type-fest";
 
 import { Icon, Icons } from "@/components/Icon";
 import { BrandPill } from "@/components/layout/BrandPill";
@@ -7,19 +9,33 @@ import { WideContainer } from "@/components/layout/WideContainer";
 import { shouldHaveDmcaPage } from "@/pages/Dmca";
 import { conf } from "@/setup/config";
 
-function FooterLink(props: {
-  href?: string;
-  onClick?: () => void;
-  children: React.ReactNode;
-  icon: Icons;
-}) {
+// to and href are mutually exclusive
+type FooterLinkProps = RequireExactlyOne<
+  {
+    children: React.ReactNode;
+    icon: Icons;
+    to: string;
+    href: string;
+  },
+  "to" | "href"
+>;
+
+function FooterLink(props: FooterLinkProps) {
+  const history = useHistory();
+
+  const navigateTo = useCallback(() => {
+    if (!props.to) return;
+
+    history.push(props.to);
+  }, [history, props.to]);
+
   return (
     <a
-      href={props.href ?? "#"}
-      target="_blank"
-      className="tabbable rounded py-2 px-3 inline-flex items-center space-x-3 transition-colors duration-200 hover:text-type-emphasis"
+      href={props.href}
+      target={props.href ? "_blank" : undefined}
       rel="noreferrer"
-      onClick={props.onClick}
+      className="tabbable rounded py-2 px-3 inline-flex cursor-pointer items-center space-x-3 transition-colors duration-200 hover:text-type-emphasis"
+      onClick={props.to ? navigateTo : undefined}
     >
       <Icon icon={props.icon} className="text-2xl" />
       <span className="font-medium">{props.children}</span>
@@ -29,12 +45,11 @@ function FooterLink(props: {
 
 function Dmca() {
   const { t } = useTranslation();
-  const history = useHistory();
 
   if (!shouldHaveDmcaPage()) return null;
 
   return (
-    <FooterLink icon={Icons.DRAGON} onClick={() => history.push("/dmca")}>
+    <FooterLink to="/dmca" icon={Icons.DRAGON}>
       {t("footer.links.dmca")}
     </FooterLink>
   );