diff --git a/client/package.json b/client/package.json
index 55e992b..4b84f39 100644
--- a/client/package.json
+++ b/client/package.json
@@ -13,10 +13,10 @@
"leaflet": "^1.9.4",
"lucide-react": "^1.14.0",
"portos-ai-toolkit": "^0.8.4",
- "react": "^18.3.1",
- "react-dom": "^18.3.1",
+ "react": "^19.2.5",
+ "react-dom": "^19.2.7",
"react-hot-toast": "^2.6.0",
- "react-leaflet": "^4.2.1",
+ "react-leaflet": "^5.0.0",
"react-router-dom": "^7.16.0",
"socket.io-client": "^4.8.3",
"zustand": "^5.0.12"
@@ -26,8 +26,8 @@
"@testing-library/react": "^16.3.2",
"@types/d3": "^7.4.3",
"@types/leaflet": "^1.9.21",
- "@types/react": "^18.3.28",
- "@types/react-dom": "^18.3.7",
+ "@types/react": "^19.2.14",
+ "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.2",
"tailwindcss": "^4.3.1",
"typescript": "^6.0.2",
diff --git a/client/src/components/ai/AiDiscoveryModal.tsx b/client/src/components/ai/AiDiscoveryModal.tsx
index 08867d2..deb3a50 100644
--- a/client/src/components/ai/AiDiscoveryModal.tsx
+++ b/client/src/components/ai/AiDiscoveryModal.tsx
@@ -372,7 +372,7 @@ export function AiDiscoveryModal({ dbId, onClose, onComplete }: AiDiscoveryModal
e.stopPropagation()}
+ onClick={(e: React.MouseEvent) => e.stopPropagation()}
className="font-medium text-app-text hover:text-app-accent transition-colors"
>
{candidate.name}
diff --git a/client/src/components/ancestry-tree/FamilyUnitCard.tsx b/client/src/components/ancestry-tree/FamilyUnitCard.tsx
index 778b80a..9014b54 100644
--- a/client/src/components/ancestry-tree/FamilyUnitCard.tsx
+++ b/client/src/components/ancestry-tree/FamilyUnitCard.tsx
@@ -1,6 +1,7 @@
import type { AncestryFamilyUnit } from '@fsf/shared';
import { PersonCard } from './PersonCard';
import { useRef, useEffect, useState } from 'react';
+import type { JSX } from 'react';
interface FamilyUnitCardProps {
unit: AncestryFamilyUnit;
diff --git a/client/src/components/person/LinkRelationshipDialog.tsx b/client/src/components/person/LinkRelationshipDialog.tsx
index 73ed512..0806750 100644
--- a/client/src/components/person/LinkRelationshipDialog.tsx
+++ b/client/src/components/person/LinkRelationshipDialog.tsx
@@ -37,7 +37,7 @@ export function LinkRelationshipDialog({ open, dbId, personId, defaultType, onCl
const [saving, setSaving] = useState(false);
const inputRef = useRef(null);
- const searchTimerRef = useRef>();
+ const searchTimerRef = useRef>(undefined);
useEffect(() => {
if (open) {
diff --git a/client/src/components/person/RelationshipModal.tsx b/client/src/components/person/RelationshipModal.tsx
index d43904b..08c4f01 100644
--- a/client/src/components/person/RelationshipModal.tsx
+++ b/client/src/components/person/RelationshipModal.tsx
@@ -38,7 +38,7 @@ export function RelationshipModal({ open, dbId, personId, initialType, onClose,
const [newName, setNewName] = useState('');
const [newGender, setNewGender] = useState<'male' | 'female' | 'unknown'>('unknown');
const inputRef = useRef(null);
- const debounceRef = useRef>();
+ const debounceRef = useRef>(undefined);
// Monotonically increasing request id so out-of-order responses from
// earlier searches don't overwrite results from a newer search.
const searchRequestIdRef = useRef(0);
diff --git a/client/src/components/ui/CopyButton.tsx b/client/src/components/ui/CopyButton.tsx
index 05a9ff3..8a7ab2a 100644
--- a/client/src/components/ui/CopyButton.tsx
+++ b/client/src/components/ui/CopyButton.tsx
@@ -11,7 +11,7 @@ interface CopyButtonProps {
export function CopyButton({ text, label = 'Copied!', size = 14, className = '' }: CopyButtonProps) {
const [copied, setCopied] = useState(false);
- const timeoutRef = useRef>();
+ const timeoutRef = useRef>(undefined);
useEffect(() => {
return () => clearTimeout(timeoutRef.current);
diff --git a/package-lock.json b/package-lock.json
index e0cc02d..a38a786 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -40,10 +40,10 @@
"leaflet": "^1.9.4",
"lucide-react": "^1.14.0",
"portos-ai-toolkit": "^0.8.4",
- "react": "^18.3.1",
- "react-dom": "^18.3.1",
+ "react": "^19.2.5",
+ "react-dom": "^19.2.7",
"react-hot-toast": "^2.6.0",
- "react-leaflet": "^4.2.1",
+ "react-leaflet": "^5.0.0",
"react-router-dom": "^7.16.0",
"socket.io-client": "^4.8.3",
"zustand": "^5.0.12"
@@ -53,14 +53,86 @@
"@testing-library/react": "^16.3.2",
"@types/d3": "^7.4.3",
"@types/leaflet": "^1.9.21",
- "@types/react": "^18.3.28",
- "@types/react-dom": "^18.3.7",
+ "@types/react": "^19.2.14",
+ "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.2",
"tailwindcss": "^4.3.1",
"typescript": "^6.0.2",
"vite": "^8.0.10"
}
},
+ "client/node_modules/@react-leaflet/core": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz",
+ "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==",
+ "license": "Hippocratic-2.1",
+ "peerDependencies": {
+ "leaflet": "^1.9.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ }
+ },
+ "client/node_modules/@types/react": {
+ "version": "19.2.17",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
+ "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "client/node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "client/node_modules/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "client/node_modules/react-dom": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
+ "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.7"
+ }
+ },
+ "client/node_modules/react-leaflet": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz",
+ "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==",
+ "license": "Hippocratic-2.1",
+ "dependencies": {
+ "@react-leaflet/core": "^3.0.0"
+ },
+ "peerDependencies": {
+ "leaflet": "^1.9.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ }
+ },
+ "client/node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
"node_modules/@babel/code-frame": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
@@ -1224,17 +1296,6 @@
"debug": "^4.3.1"
}
},
- "node_modules/@react-leaflet/core": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
- "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
- "license": "Hippocratic-2.1",
- "peerDependencies": {
- "leaflet": "^1.9.0",
- "react": "^18.0.0",
- "react-dom": "^18.0.0"
- }
- },
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz",
@@ -2320,13 +2381,6 @@
"undici-types": ">=7.24.0 <7.24.7"
}
},
- "node_modules/@types/prop-types": {
- "version": "15.7.15",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
- "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
- "devOptional": true,
- "license": "MIT"
- },
"node_modules/@types/qs": {
"version": "6.15.1",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz",
@@ -2341,27 +2395,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/react": {
- "version": "18.3.31",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz",
- "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
- "devOptional": true,
- "license": "MIT",
- "dependencies": {
- "@types/prop-types": "*",
- "csstype": "^3.2.2"
- }
- },
- "node_modules/@types/react-dom": {
- "version": "18.3.7",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
- "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "@types/react": "^18.0.0"
- }
- },
"node_modules/@types/send": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
@@ -5752,6 +5785,7 @@
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
@@ -5763,7 +5797,8 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/lru-cache": {
"version": "7.18.3",
@@ -6877,6 +6912,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -6889,6 +6925,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
@@ -6922,20 +6959,6 @@
"license": "MIT",
"peer": true
},
- "node_modules/react-leaflet": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
- "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
- "license": "Hippocratic-2.1",
- "dependencies": {
- "@react-leaflet/core": "^2.1.0"
- },
- "peerDependencies": {
- "leaflet": "^1.9.0",
- "react": "^18.0.0",
- "react-dom": "^18.0.0"
- }
- },
"node_modules/react-router": {
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.17.0.tgz",
@@ -7311,6 +7334,7 @@
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
}