From bcd8257fda44678e53e184eea8ab3a97709e1d02 Mon Sep 17 00:00:00 2001
From: Anderson Shindy Oki <anderson.vs.oki@gmail.com>
Date: Sat, 8 Jun 2024 09:49:40 +0900
Subject: no log: Apply style linter (#2516)

---
 frontend/src/components/async/MutateAction.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'frontend/src/components/async/MutateAction.tsx')

diff --git a/frontend/src/components/async/MutateAction.tsx b/frontend/src/components/async/MutateAction.tsx
index 920fe4ff3..9a5837c02 100644
--- a/frontend/src/components/async/MutateAction.tsx
+++ b/frontend/src/components/async/MutateAction.tsx
@@ -1,7 +1,7 @@
 import { useCallback, useState } from "react";
 import { UseMutationResult } from "react-query";
-import { Action } from "../inputs";
-import { ActionProps } from "../inputs/Action";
+import { Action } from "@/components/inputs";
+import { ActionProps } from "@/components/inputs/Action";
 
 type MutateActionProps<DATA, VAR> = Omit<
   ActionProps,
-- 
cgit v1.2.3


From 4d3c1f4b9d517ef3f64e489d011b939bc816d228 Mon Sep 17 00:00:00 2001
From: Anderson Shindy Oki <anderson.vs.oki@gmail.com>
Date: Tue, 11 Jun 2024 10:37:14 +0900
Subject: Upgraded react-query to v5

---
 frontend/config/chunks.ts                          |   3 +-
 frontend/package-lock.json                         | 179 +++++------
 frontend/package.json                              |   3 +-
 frontend/src/apis/hooks/episodes.ts                | 119 ++++----
 frontend/src/apis/hooks/histories.ts               |  15 +-
 frontend/src/apis/hooks/languages.ts               |  26 +-
 frontend/src/apis/hooks/movies.ts                  | 155 ++++++----
 frontend/src/apis/hooks/providers.ts               | 105 ++++---
 frontend/src/apis/hooks/series.ts                  |  91 +++---
 frontend/src/apis/hooks/status.ts                  |  20 +-
 frontend/src/apis/hooks/subtitles.ts               | 188 +++++++-----
 frontend/src/apis/hooks/system.ts                  | 327 +++++++++++----------
 frontend/src/apis/queries/hooks.ts                 |  34 +--
 frontend/src/apis/queries/index.ts                 |   8 +-
 frontend/src/components/async/MutateAction.tsx     |   2 +-
 frontend/src/components/async/MutateButton.tsx     |   2 +-
 frontend/src/components/async/QueryOverlay.tsx     |   2 +-
 frontend/src/components/forms/ItemEditForm.tsx     |   6 +-
 .../src/components/modals/ManualSearchModal.tsx    |   2 +-
 frontend/src/modules/socketio/index.ts             |   2 +-
 frontend/src/modules/socketio/reducer.ts           |  79 +++--
 .../pages/Settings/Notifications/components.tsx    |   6 +-
 frontend/src/pages/Settings/components/Layout.tsx  |   2 +-
 .../src/pages/Settings/components/LayoutModal.tsx  |   2 +-
 frontend/src/pages/System/Backups/index.tsx        |   2 +-
 frontend/src/pages/System/Logs/index.tsx           |   6 +-
 frontend/src/pages/System/Providers/index.tsx      |   2 +-
 frontend/src/pages/Wanted/Movies/index.tsx         |   2 +-
 frontend/src/pages/Wanted/Series/index.tsx         |   2 +-
 frontend/src/pages/views/MassEditor.tsx            |   2 +-
 frontend/src/providers.tsx                         |   4 +-
 31 files changed, 778 insertions(+), 620 deletions(-)

(limited to 'frontend/src/components/async/MutateAction.tsx')

diff --git a/frontend/config/chunks.ts b/frontend/config/chunks.ts
index c5c5f7fba..6dc7f3772 100644
--- a/frontend/config/chunks.ts
+++ b/frontend/config/chunks.ts
@@ -1,10 +1,11 @@
+// eslint-disable-next-line no-restricted-imports
 import { dependencies } from "../package.json";
 
 const vendors = [
   "react",
   "react-router-dom",
   "react-dom",
-  "react-query",
+  "@tanstack/react-query",
   "axios",
   "socket.io-client",
 ];
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 39b987174..298bd7781 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -15,10 +15,10 @@
         "@mantine/hooks": "^7.10.1",
         "@mantine/modals": "^7.10.1",
         "@mantine/notifications": "^7.10.1",
+        "@tanstack/react-query": "^5.40.1",
         "axios": "^1.6.8",
         "react": "^18.2.0",
         "react-dom": "^18.2.0",
-        "react-query": "^3.39.3",
         "react-router-dom": "^6.23.1",
         "socket.io-client": "^4.7.5"
       },
@@ -29,6 +29,7 @@
         "@fortawesome/free-regular-svg-icons": "^6.5.2",
         "@fortawesome/free-solid-svg-icons": "^6.5.2",
         "@fortawesome/react-fontawesome": "^0.2.0",
+        "@tanstack/react-query-devtools": "^5.40.1",
         "@testing-library/jest-dom": "^6.4.2",
         "@testing-library/react": "^15.0.5",
         "@testing-library/user-event": "^14.5.2",
@@ -3331,6 +3332,61 @@
       "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
       "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
     },
+    "node_modules/@tanstack/query-core": {
+      "version": "5.40.0",
+      "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.40.0.tgz",
+      "integrity": "sha512-eD8K8jsOIq0Z5u/QbvOmfvKKE/XC39jA7yv4hgpl/1SRiU+J8QCIwgM/mEHuunQsL87dcvnHqSVLmf9pD4CiaA==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      }
+    },
+    "node_modules/@tanstack/query-devtools": {
+      "version": "5.37.1",
+      "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.37.1.tgz",
+      "integrity": "sha512-XcG4IIHIv0YQKrexTqo2zogQWR1Sz672tX2KsfE9kzB+9zhx44vRKH5si4WDILE1PIWQpStFs/NnrDQrBAUQpg==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      }
+    },
+    "node_modules/@tanstack/react-query": {
+      "version": "5.40.1",
+      "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.40.1.tgz",
+      "integrity": "sha512-gOcmu+gpFd2taHrrgMM9RemLYYEDYfsCqszxCC0xtx+csDa4R8t7Hr7SfWXQP13S2sF+mOxySo/+FNXJFYBqcA==",
+      "license": "MIT",
+      "dependencies": {
+        "@tanstack/query-core": "5.40.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      },
+      "peerDependencies": {
+        "react": "^18.0.0"
+      }
+    },
+    "node_modules/@tanstack/react-query-devtools": {
+      "version": "5.40.1",
+      "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.40.1.tgz",
+      "integrity": "sha512-/AN2UsbuL+28/KSlBkVHq/4chHTEp4l2UWTKWixXbn4pprLQrZGmQTAKN4tYxZDuNwNZY5+Zp67pDfXj+F/UBA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@tanstack/query-devtools": "5.37.1"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      },
+      "peerDependencies": {
+        "@tanstack/react-query": "^5.40.1",
+        "react": "^18 || ^19"
+      }
+    },
     "node_modules/@testing-library/dom": {
       "version": "10.0.0",
       "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.0.0.tgz",
@@ -4726,15 +4782,8 @@
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
-    },
-    "node_modules/big-integer": {
-      "version": "1.6.52",
-      "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
-      "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==",
-      "engines": {
-        "node": ">=0.6"
-      }
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
     },
     "node_modules/binary-extensions": {
       "version": "2.3.0",
@@ -4752,6 +4801,7 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
       "dependencies": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -4769,21 +4819,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/broadcast-channel": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz",
-      "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==",
-      "dependencies": {
-        "@babel/runtime": "^7.7.2",
-        "detect-node": "^2.1.0",
-        "js-sha3": "0.8.0",
-        "microseconds": "0.2.0",
-        "nano-time": "1.0.0",
-        "oblivious-set": "1.0.0",
-        "rimraf": "3.0.2",
-        "unload": "2.2.0"
-      }
-    },
     "node_modules/browserslist": {
       "version": "4.23.0",
       "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
@@ -5029,7 +5064,8 @@
     "node_modules/concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true
     },
     "node_modules/confusing-browser-globals": {
       "version": "1.0.11",
@@ -5412,11 +5448,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/detect-node": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
-      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="
-    },
     "node_modules/detect-node-es": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
@@ -6492,7 +6523,8 @@
     "node_modules/fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true
     },
     "node_modules/fsevents": {
       "version": "2.3.3",
@@ -6622,6 +6654,7 @@
       "version": "7.2.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
       "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dev": true,
       "dependencies": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
@@ -6931,6 +6964,7 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dev": true,
       "dependencies": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -6939,7 +6973,8 @@
     "node_modules/inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
     },
     "node_modules/internal-slot": {
       "version": "1.0.7",
@@ -7601,11 +7636,6 @@
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
-    "node_modules/js-sha3": {
-      "version": "0.8.0",
-      "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
-      "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
-    },
     "node_modules/js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -7965,15 +7995,6 @@
       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
       "dev": true
     },
-    "node_modules/match-sorter": {
-      "version": "6.3.4",
-      "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz",
-      "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==",
-      "dependencies": {
-        "@babel/runtime": "^7.23.8",
-        "remove-accents": "0.5.0"
-      }
-    },
     "node_modules/merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -8002,11 +8023,6 @@
         "node": ">=8.6"
       }
     },
-    "node_modules/microseconds": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz",
-      "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA=="
-    },
     "node_modules/mime-db": {
       "version": "1.52.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -8048,6 +8064,7 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
       "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
       "dependencies": {
         "brace-expansion": "^1.1.7"
       },
@@ -8099,14 +8116,6 @@
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
-    "node_modules/nano-time": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz",
-      "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==",
-      "dependencies": {
-        "big-integer": "^1.6.16"
-      }
-    },
     "node_modules/nanoid": {
       "version": "3.3.7",
       "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@@ -8294,15 +8303,11 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/oblivious-set": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz",
-      "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw=="
-    },
     "node_modules/once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
       "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
       "dependencies": {
         "wrappy": "1"
       }
@@ -8424,6 +8429,7 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
       "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -8857,31 +8863,6 @@
         "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
       }
     },
-    "node_modules/react-query": {
-      "version": "3.39.3",
-      "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz",
-      "integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==",
-      "dependencies": {
-        "@babel/runtime": "^7.5.5",
-        "broadcast-channel": "^3.4.1",
-        "match-sorter": "^6.0.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/tannerlinsley"
-      },
-      "peerDependencies": {
-        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
-      },
-      "peerDependenciesMeta": {
-        "react-dom": {
-          "optional": true
-        },
-        "react-native": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/react-refresh": {
       "version": "0.14.0",
       "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
@@ -9213,11 +9194,6 @@
         "jsesc": "bin/jsesc"
       }
     },
-    "node_modules/remove-accents": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
-      "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="
-    },
     "node_modules/requires-port": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@@ -9264,6 +9240,7 @@
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
       "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
       "dependencies": {
         "glob": "^7.1.3"
       },
@@ -10154,15 +10131,6 @@
         "node": ">= 4.0.0"
       }
     },
-    "node_modules/unload": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz",
-      "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==",
-      "dependencies": {
-        "@babel/runtime": "^7.6.2",
-        "detect-node": "^2.0.4"
-      }
-    },
     "node_modules/update-browserslist-db": {
       "version": "1.0.13",
       "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
@@ -10974,7 +10942,8 @@
     "node_modules/wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true
     },
     "node_modules/ws": {
       "version": "8.16.0",
diff --git a/frontend/package.json b/frontend/package.json
index f2b10bfb4..1c1f2e039 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -19,10 +19,10 @@
     "@mantine/hooks": "^7.10.1",
     "@mantine/modals": "^7.10.1",
     "@mantine/notifications": "^7.10.1",
+    "@tanstack/react-query": "^5.40.1",
     "axios": "^1.6.8",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
-    "react-query": "^3.39.3",
     "react-router-dom": "^6.23.1",
     "socket.io-client": "^4.7.5"
   },
@@ -33,6 +33,7 @@
     "@fortawesome/free-regular-svg-icons": "^6.5.2",
     "@fortawesome/free-solid-svg-icons": "^6.5.2",
     "@fortawesome/react-fontawesome": "^0.2.0",
+    "@tanstack/react-query-devtools": "^5.40.1",
     "@testing-library/jest-dom": "^6.4.2",
     "@testing-library/react": "^15.0.5",
     "@testing-library/user-event": "^14.5.2",
diff --git a/frontend/src/apis/hooks/episodes.ts b/frontend/src/apis/hooks/episodes.ts
index ae721333a..6a489e938 100644
--- a/frontend/src/apis/hooks/episodes.ts
+++ b/frontend/src/apis/hooks/episodes.ts
@@ -1,9 +1,10 @@
+import { useEffect } from "react";
 import {
   QueryClient,
   useMutation,
   useQuery,
   useQueryClient,
-} from "react-query";
+} from "@tanstack/react-query";
 import { usePaginationQuery } from "@/apis/queries/hooks";
 import { QueryKeys } from "@/apis/queries/keys";
 import api from "@/apis/raw";
@@ -26,28 +27,36 @@ const cacheEpisodes = (client: QueryClient, episodes: Item.Episode[]) => {
 
 export function useEpisodesByIds(ids: number[]) {
   const client = useQueryClient();
-  return useQuery(
-    [QueryKeys.Series, QueryKeys.Episodes, ids],
-    () => api.episodes.byEpisodeId(ids),
-    {
-      onSuccess: (data) => {
-        cacheEpisodes(client, data);
-      },
-    },
-  );
+
+  const query = useQuery({
+    queryKey: [QueryKeys.Series, QueryKeys.Episodes, ids],
+    queryFn: () => api.episodes.byEpisodeId(ids),
+  });
+
+  useEffect(() => {
+    if (query.isSuccess && query.data) {
+      cacheEpisodes(client, query.data);
+    }
+  }, [query.isSuccess, query.data, client]);
+
+  return query;
 }
 
 export function useEpisodesBySeriesId(id: number) {
   const client = useQueryClient();
-  return useQuery(
-    [QueryKeys.Series, id, QueryKeys.Episodes, QueryKeys.All],
-    () => api.episodes.bySeriesId([id]),
-    {
-      onSuccess: (data) => {
-        cacheEpisodes(client, data);
-      },
-    },
-  );
+
+  const query = useQuery({
+    queryKey: [QueryKeys.Series, id, QueryKeys.Episodes, QueryKeys.All],
+    queryFn: () => api.episodes.bySeriesId([id]),
+  });
+
+  useEffect(() => {
+    if (query.isSuccess && query.data) {
+      cacheEpisodes(client, query.data);
+    }
+  }, [query.isSuccess, query.data, client]);
+
+  return query;
 }
 
 export function useEpisodeWantedPagination() {
@@ -57,17 +66,18 @@ export function useEpisodeWantedPagination() {
 }
 
 export function useEpisodeBlacklist() {
-  return useQuery(
-    [QueryKeys.Series, QueryKeys.Episodes, QueryKeys.Blacklist],
-    () => api.episodes.blacklist(),
-  );
+  return useQuery({
+    queryKey: [QueryKeys.Series, QueryKeys.Episodes, QueryKeys.Blacklist],
+    queryFn: () => api.episodes.blacklist(),
+  });
 }
 
 export function useEpisodeAddBlacklist() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.Series, QueryKeys.Episodes, QueryKeys.Blacklist],
-    (param: {
+  return useMutation({
+    mutationKey: [QueryKeys.Series, QueryKeys.Episodes, QueryKeys.Blacklist],
+
+    mutationFn: (param: {
       seriesId: number;
       episodeId: number;
       form: FormType.AddBlacklist;
@@ -75,35 +85,32 @@ export function useEpisodeAddBlacklist() {
       const { seriesId, episodeId, form } = param;
       return api.episodes.addBlacklist(seriesId, episodeId, form);
     },
-    {
-      onSuccess: (_, { seriesId, episodeId }) => {
-        client.invalidateQueries([
-          QueryKeys.Series,
-          QueryKeys.Episodes,
-          QueryKeys.Blacklist,
-        ]);
-        client.invalidateQueries([QueryKeys.Series, seriesId]);
-      },
+
+    onSuccess: (_, { seriesId }) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Series, QueryKeys.Episodes, QueryKeys.Blacklist],
+      });
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Series, seriesId],
+      });
     },
-  );
+  });
 }
 
 export function useEpisodeDeleteBlacklist() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.Series, QueryKeys.Episodes, QueryKeys.Blacklist],
-    (param: { all?: boolean; form?: FormType.DeleteBlacklist }) =>
+  return useMutation({
+    mutationKey: [QueryKeys.Series, QueryKeys.Episodes, QueryKeys.Blacklist],
+
+    mutationFn: (param: { all?: boolean; form?: FormType.DeleteBlacklist }) =>
       api.episodes.deleteBlacklist(param.all, param.form),
-    {
-      onSuccess: (_, param) => {
-        client.invalidateQueries([
-          QueryKeys.Series,
-          QueryKeys.Episodes,
-          QueryKeys.Blacklist,
-        ]);
-      },
+
+    onSuccess: (_) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Series, QueryKeys.Episodes, QueryKeys.Blacklist],
+      });
     },
-  );
+  });
 }
 
 export function useEpisodeHistoryPagination() {
@@ -115,12 +122,20 @@ export function useEpisodeHistoryPagination() {
 }
 
 export function useEpisodeHistory(episodeId?: number) {
-  return useQuery(
-    [QueryKeys.Series, QueryKeys.Episodes, QueryKeys.History, episodeId],
-    () => {
+  return useQuery({
+    queryKey: [
+      QueryKeys.Series,
+      QueryKeys.Episodes,
+      QueryKeys.History,
+      episodeId,
+    ],
+
+    queryFn: () => {
       if (episodeId) {
         return api.episodes.historyBy(episodeId);
       }
+
+      return [];
     },
-  );
+  });
 }
diff --git a/frontend/src/apis/hooks/histories.ts b/frontend/src/apis/hooks/histories.ts
index 7e3c188e0..6368d11db 100644
--- a/frontend/src/apis/hooks/histories.ts
+++ b/frontend/src/apis/hooks/histories.ts
@@ -1,4 +1,4 @@
-import { useQuery } from "react-query";
+import { useQuery } from "@tanstack/react-query";
 import { QueryKeys } from "@/apis/queries/keys";
 import api from "@/apis/raw";
 
@@ -8,14 +8,19 @@ export function useHistoryStats(
   provider: System.Provider | null,
   language: Language.Info | null,
 ) {
-  return useQuery(
-    [QueryKeys.System, QueryKeys.History, { time, action, provider, language }],
-    () =>
+  return useQuery({
+    queryKey: [
+      QueryKeys.System,
+      QueryKeys.History,
+      { time, action, provider, language },
+    ],
+
+    queryFn: () =>
       api.history.stats(
         time,
         action ?? undefined,
         provider?.name,
         language?.code2,
       ),
-  );
+  });
 }
diff --git a/frontend/src/apis/hooks/languages.ts b/frontend/src/apis/hooks/languages.ts
index 1a639ab15..4cbdce69b 100644
--- a/frontend/src/apis/hooks/languages.ts
+++ b/frontend/src/apis/hooks/languages.ts
@@ -1,23 +1,19 @@
-import { useQuery } from "react-query";
+import { useQuery } from "@tanstack/react-query";
 import { QueryKeys } from "@/apis/queries/keys";
 import api from "@/apis/raw";
 
 export function useLanguages(history?: boolean) {
-  return useQuery(
-    [QueryKeys.System, QueryKeys.Languages, history ?? false],
-    () => api.system.languages(history),
-    {
-      staleTime: Infinity,
-    },
-  );
+  return useQuery({
+    queryKey: [QueryKeys.System, QueryKeys.Languages, history ?? false],
+    queryFn: () => api.system.languages(history),
+    staleTime: Infinity,
+  });
 }
 
 export function useLanguageProfiles() {
-  return useQuery(
-    [QueryKeys.System, QueryKeys.LanguagesProfiles],
-    () => api.system.languagesProfileList(),
-    {
-      staleTime: Infinity,
-    },
-  );
+  return useQuery({
+    queryKey: [QueryKeys.System, QueryKeys.LanguagesProfiles],
+    queryFn: () => api.system.languagesProfileList(),
+    staleTime: Infinity,
+  });
 }
diff --git a/frontend/src/apis/hooks/movies.ts b/frontend/src/apis/hooks/movies.ts
index 344f2db04..cf4594cbe 100644
--- a/frontend/src/apis/hooks/movies.ts
+++ b/frontend/src/apis/hooks/movies.ts
@@ -1,9 +1,10 @@
+import { useEffect } from "react";
 import {
   QueryClient,
   useMutation,
   useQuery,
   useQueryClient,
-} from "react-query";
+} from "@tanstack/react-query";
 import { usePaginationQuery } from "@/apis/queries/hooks";
 import { QueryKeys } from "@/apis/queries/keys";
 import api from "@/apis/raw";
@@ -16,31 +17,47 @@ const cacheMovies = (client: QueryClient, movies: Item.Movie[]) => {
 
 export function useMoviesByIds(ids: number[]) {
   const client = useQueryClient();
-  return useQuery([QueryKeys.Movies, ...ids], () => api.movies.movies(ids), {
-    onSuccess: (data) => {
-      cacheMovies(client, data);
-    },
+
+  const query = useQuery({
+    queryKey: [QueryKeys.Movies, ...ids],
+    queryFn: () => api.movies.movies(ids),
   });
+
+  useEffect(() => {
+    if (query.isSuccess && query.data) {
+      cacheMovies(client, query.data);
+    }
+  }, [query.isSuccess, query.data, client]);
+
+  return query;
 }
 
 export function useMovieById(id: number) {
-  return useQuery([QueryKeys.Movies, id], async () => {
-    const response = await api.movies.movies([id]);
-    return response.length > 0 ? response[0] : undefined;
+  return useQuery({
+    queryKey: [QueryKeys.Movies, id],
+
+    queryFn: async () => {
+      const response = await api.movies.movies([id]);
+      return response.length > 0 ? response[0] : undefined;
+    },
   });
 }
 
 export function useMovies() {
   const client = useQueryClient();
-  return useQuery(
-    [QueryKeys.Movies, QueryKeys.All],
-    () => api.movies.movies(),
-    {
-      onSuccess: (data) => {
-        cacheMovies(client, data);
-      },
-    },
-  );
+
+  const query = useQuery({
+    queryKey: [QueryKeys.Movies, QueryKeys.All],
+    queryFn: () => api.movies.movies(),
+  });
+
+  useEffect(() => {
+    if (query.isSuccess && query.data) {
+      cacheMovies(client, query.data);
+    }
+  }, [query.isSuccess, query.data, client]);
+
+  return query;
 }
 
 export function useMoviesPagination() {
@@ -51,32 +68,36 @@ export function useMoviesPagination() {
 
 export function useMovieModification() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.Movies],
-    (form: FormType.ModifyItem) => api.movies.modify(form),
-    {
-      onSuccess: (_, form) => {
-        form.id.forEach((v) => {
-          client.invalidateQueries([QueryKeys.Movies, v]);
+  return useMutation({
+    mutationKey: [QueryKeys.Movies],
+    mutationFn: (form: FormType.ModifyItem) => api.movies.modify(form),
+
+    onSuccess: (_, form) => {
+      form.id.forEach((v) => {
+        client.invalidateQueries({
+          queryKey: [QueryKeys.Movies, v],
         });
-        // TODO: query less
-        client.invalidateQueries([QueryKeys.Movies]);
-      },
+      });
+      // TODO: query less
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Movies],
+      });
     },
-  );
+  });
 }
 
 export function useMovieAction() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.Actions, QueryKeys.Movies],
-    (form: FormType.MoviesAction) => api.movies.action(form),
-    {
-      onSuccess: () => {
-        client.invalidateQueries([QueryKeys.Movies]);
-      },
+  return useMutation({
+    mutationKey: [QueryKeys.Actions, QueryKeys.Movies],
+    mutationFn: (form: FormType.MoviesAction) => api.movies.action(form),
+
+    onSuccess: () => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Movies],
+      });
     },
-  );
+  });
 }
 
 export function useMovieWantedPagination() {
@@ -86,40 +107,48 @@ export function useMovieWantedPagination() {
 }
 
 export function useMovieBlacklist() {
-  return useQuery([QueryKeys.Movies, QueryKeys.Blacklist], () =>
-    api.movies.blacklist(),
-  );
+  return useQuery({
+    queryKey: [QueryKeys.Movies, QueryKeys.Blacklist],
+
+    queryFn: () => api.movies.blacklist(),
+  });
 }
 
 export function useMovieAddBlacklist() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.Movies, QueryKeys.Blacklist],
-    (param: { id: number; form: FormType.AddBlacklist }) => {
+  return useMutation({
+    mutationKey: [QueryKeys.Movies, QueryKeys.Blacklist],
+
+    mutationFn: (param: { id: number; form: FormType.AddBlacklist }) => {
       const { id, form } = param;
       return api.movies.addBlacklist(id, form);
     },
-    {
-      onSuccess: (_, { id }) => {
-        client.invalidateQueries([QueryKeys.Movies, QueryKeys.Blacklist]);
-        client.invalidateQueries([QueryKeys.Movies, id]);
-      },
+
+    onSuccess: (_, { id }) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Movies, QueryKeys.Blacklist],
+      });
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Movies, id],
+      });
     },
-  );
+  });
 }
 
 export function useMovieDeleteBlacklist() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.Movies, QueryKeys.Blacklist],
-    (param: { all?: boolean; form?: FormType.DeleteBlacklist }) =>
+  return useMutation({
+    mutationKey: [QueryKeys.Movies, QueryKeys.Blacklist],
+
+    mutationFn: (param: { all?: boolean; form?: FormType.DeleteBlacklist }) =>
       api.movies.deleteBlacklist(param.all, param.form),
-    {
-      onSuccess: (_, param) => {
-        client.invalidateQueries([QueryKeys.Movies, QueryKeys.Blacklist]);
-      },
+
+    onSuccess: (_, param) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Movies, QueryKeys.Blacklist],
+      });
     },
-  );
+  });
 }
 
 export function useMovieHistoryPagination() {
@@ -131,9 +160,15 @@ export function useMovieHistoryPagination() {
 }
 
 export function useMovieHistory(radarrId?: number) {
-  return useQuery([QueryKeys.Movies, QueryKeys.History, radarrId], () => {
-    if (radarrId) {
-      return api.movies.historyBy(radarrId);
-    }
+  return useQuery({
+    queryKey: [QueryKeys.Movies, QueryKeys.History, radarrId],
+
+    queryFn: () => {
+      if (radarrId) {
+        return api.movies.historyBy(radarrId);
+      }
+
+      return [];
+    },
   });
 }
diff --git a/frontend/src/apis/hooks/providers.ts b/frontend/src/apis/hooks/providers.ts
index a96d9ba3c..b40a24e2f 100644
--- a/frontend/src/apis/hooks/providers.ts
+++ b/frontend/src/apis/hooks/providers.ts
@@ -1,66 +1,82 @@
-import { useMutation, useQuery, useQueryClient } from "react-query";
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
 import { QueryKeys } from "@/apis/queries/keys";
 import api from "@/apis/raw";
 
 export function useSystemProviders(history?: boolean) {
-  return useQuery(
-    [QueryKeys.System, QueryKeys.Providers, history ?? false],
-    () => api.providers.providers(history),
-  );
+  return useQuery({
+    queryKey: [QueryKeys.System, QueryKeys.Providers, history ?? false],
+    queryFn: () => api.providers.providers(history),
+  });
 }
 
 export function useMoviesProvider(radarrId?: number) {
-  return useQuery(
-    [QueryKeys.System, QueryKeys.Providers, QueryKeys.Movies, radarrId],
-    () => {
+  return useQuery({
+    queryKey: [
+      QueryKeys.System,
+      QueryKeys.Providers,
+      QueryKeys.Movies,
+      radarrId,
+    ],
+
+    queryFn: () => {
       if (radarrId) {
         return api.providers.movies(radarrId);
       }
+
+      return [];
     },
-    {
-      staleTime: 0,
-    },
-  );
+
+    staleTime: 0,
+  });
 }
 
 export function useEpisodesProvider(episodeId?: number) {
-  return useQuery(
-    [QueryKeys.System, QueryKeys.Providers, QueryKeys.Episodes, episodeId],
-    () => {
+  return useQuery({
+    queryKey: [
+      QueryKeys.System,
+      QueryKeys.Providers,
+      QueryKeys.Episodes,
+      episodeId,
+    ],
+
+    queryFn: () => {
       if (episodeId) {
         return api.providers.episodes(episodeId);
       }
+
+      return [];
     },
-    {
-      staleTime: 0,
-    },
-  );
+
+    staleTime: 0,
+  });
 }
 
 export function useResetProvider() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.System, QueryKeys.Providers],
-    () => api.providers.reset(),
-    {
-      onSuccess: () => {
-        client.invalidateQueries([QueryKeys.System, QueryKeys.Providers]);
-      },
+  return useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Providers],
+    mutationFn: () => api.providers.reset(),
+
+    onSuccess: () => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.System, QueryKeys.Providers],
+      });
     },
-  );
+  });
 }
 
 export function useDownloadEpisodeSubtitles() {
   const client = useQueryClient();
 
-  return useMutation(
-    [
+  return useMutation({
+    mutationKey: [
       QueryKeys.System,
       QueryKeys.Providers,
       QueryKeys.Subtitles,
       QueryKeys.Episodes,
     ],
-    (param: {
+
+    mutationFn: (param: {
       seriesId: number;
       episodeId: number;
       form: FormType.ManualDownload;
@@ -70,30 +86,33 @@ export function useDownloadEpisodeSubtitles() {
         param.episodeId,
         param.form,
       ),
-    {
-      onSuccess: (_, param) => {
-        client.invalidateQueries([QueryKeys.Series, param.seriesId]);
-      },
+
+    onSuccess: (_, param) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Series, param.seriesId],
+      });
     },
-  );
+  });
 }
 
 export function useDownloadMovieSubtitles() {
   const client = useQueryClient();
 
-  return useMutation(
-    [
+  return useMutation({
+    mutationKey: [
       QueryKeys.System,
       QueryKeys.Providers,
       QueryKeys.Subtitles,
       QueryKeys.Movies,
     ],
-    (param: { radarrId: number; form: FormType.ManualDownload }) =>
+
+    mutationFn: (param: { radarrId: number; form: FormType.ManualDownload }) =>
       api.providers.downloadMovieSubtitle(param.radarrId, param.form),
-    {
-      onSuccess: (_, param) => {
-        client.invalidateQueries([QueryKeys.Movies, param.radarrId]);
-      },
+
+    onSuccess: (_, param) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Movies, param.radarrId],
+      });
     },
-  );
+  });
 }
diff --git a/frontend/src/apis/hooks/series.ts b/frontend/src/apis/hooks/series.ts
index d27773973..2add91194 100644
--- a/frontend/src/apis/hooks/series.ts
+++ b/frontend/src/apis/hooks/series.ts
@@ -1,9 +1,10 @@
+import { useEffect } from "react";
 import {
   QueryClient,
   useMutation,
   useQuery,
   useQueryClient,
-} from "react-query";
+} from "@tanstack/react-query";
 import { usePaginationQuery } from "@/apis/queries/hooks";
 import { QueryKeys } from "@/apis/queries/keys";
 import api from "@/apis/raw";
@@ -16,31 +17,47 @@ function cacheSeries(client: QueryClient, series: Item.Series[]) {
 
 export function useSeriesByIds(ids: number[]) {
   const client = useQueryClient();
-  return useQuery([QueryKeys.Series, ...ids], () => api.series.series(ids), {
-    onSuccess: (data) => {
-      cacheSeries(client, data);
-    },
+
+  const query = useQuery({
+    queryKey: [QueryKeys.Series, ...ids],
+    queryFn: () => api.series.series(ids),
   });
+
+  useEffect(() => {
+    if (query.isSuccess && query.data) {
+      cacheSeries(client, query.data);
+    }
+  }, [query.isSuccess, query.data, client]);
+
+  return query;
 }
 
 export function useSeriesById(id: number) {
-  return useQuery([QueryKeys.Series, id], async () => {
-    const response = await api.series.series([id]);
-    return response.length > 0 ? response[0] : undefined;
+  return useQuery({
+    queryKey: [QueryKeys.Series, id],
+
+    queryFn: async () => {
+      const response = await api.series.series([id]);
+      return response.length > 0 ? response[0] : undefined;
+    },
   });
 }
 
 export function useSeries() {
   const client = useQueryClient();
-  return useQuery(
-    [QueryKeys.Series, QueryKeys.All],
-    () => api.series.series(),
-    {
-      onSuccess: (data) => {
-        cacheSeries(client, data);
-      },
-    },
-  );
+
+  const query = useQuery({
+    queryKey: [QueryKeys.Series, QueryKeys.All],
+    queryFn: () => api.series.series(),
+  });
+
+  useEffect(() => {
+    if (query.isSuccess && query.data) {
+      cacheSeries(client, query.data);
+    }
+  }, [query.isSuccess, query.data, client]);
+
+  return query;
 }
 
 export function useSeriesPagination() {
@@ -51,29 +68,33 @@ export function useSeriesPagination() {
 
 export function useSeriesModification() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.Series],
-    (form: FormType.ModifyItem) => api.series.modify(form),
-    {
-      onSuccess: (_, form) => {
-        form.id.forEach((v) => {
-          client.invalidateQueries([QueryKeys.Series, v]);
+  return useMutation({
+    mutationKey: [QueryKeys.Series],
+    mutationFn: (form: FormType.ModifyItem) => api.series.modify(form),
+
+    onSuccess: (_, form) => {
+      form.id.forEach((v) => {
+        client.invalidateQueries({
+          queryKey: [QueryKeys.Series, v],
         });
-        client.invalidateQueries([QueryKeys.Series]);
-      },
+      });
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Series],
+      });
     },
-  );
+  });
 }
 
 export function useSeriesAction() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.Actions, QueryKeys.Series],
-    (form: FormType.SeriesAction) => api.series.action(form),
-    {
-      onSuccess: () => {
-        client.invalidateQueries([QueryKeys.Series]);
-      },
+  return useMutation({
+    mutationKey: [QueryKeys.Actions, QueryKeys.Series],
+    mutationFn: (form: FormType.SeriesAction) => api.series.action(form),
+
+    onSuccess: () => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Series],
+      });
     },
-  );
+  });
 }
diff --git a/frontend/src/apis/hooks/status.ts b/frontend/src/apis/hooks/status.ts
index c5e7c3501..66af943fc 100644
--- a/frontend/src/apis/hooks/status.ts
+++ b/frontend/src/apis/hooks/status.ts
@@ -1,16 +1,28 @@
-import { useIsMutating } from "react-query";
+import { useIsMutating } from "@tanstack/react-query";
 import { QueryKeys } from "@/apis/queries/keys";
 
 export function useIsAnyActionRunning() {
-  return useIsMutating([QueryKeys.Actions]) > 0;
+  return (
+    useIsMutating({
+      mutationKey: [QueryKeys.Actions],
+    }) > 0
+  );
 }
 
 export function useIsMovieActionRunning() {
-  return useIsMutating([QueryKeys.Actions, QueryKeys.Movies]) > 0;
+  return (
+    useIsMutating({
+      mutationKey: [QueryKeys.Actions, QueryKeys.Movies],
+    }) > 0
+  );
 }
 
 export function useIsSeriesActionRunning() {
-  return useIsMutating([QueryKeys.Actions, QueryKeys.Series]) > 0;
+  return (
+    useIsMutating({
+      mutationKey: [QueryKeys.Actions, QueryKeys.Series],
+    }) > 0
+  );
 }
 
 export function useIsAnyMutationRunning() {
diff --git a/frontend/src/apis/hooks/subtitles.ts b/frontend/src/apis/hooks/subtitles.ts
index 8ea572a00..dfc263ff6 100644
--- a/frontend/src/apis/hooks/subtitles.ts
+++ b/frontend/src/apis/hooks/subtitles.ts
@@ -1,4 +1,4 @@
-import { useMutation, useQuery, useQueryClient } from "react-query";
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
 import { QueryKeys } from "@/apis/queries/keys";
 import api from "@/apis/raw";
 
@@ -8,23 +8,29 @@ export function useSubtitleAction() {
     action: string;
     form: FormType.ModifySubtitle;
   }
-  return useMutation(
-    [QueryKeys.Subtitles],
-    (param: Param) => api.subtitles.modify(param.action, param.form),
-    {
-      onSuccess: (_, param) => {
-        client.invalidateQueries([QueryKeys.History]);
-
-        // TODO: Query less
-        const { type, id } = param.form;
-        if (type === "episode") {
-          client.invalidateQueries([QueryKeys.Series, id]);
-        } else {
-          client.invalidateQueries([QueryKeys.Movies, id]);
-        }
-      },
+  return useMutation({
+    mutationKey: [QueryKeys.Subtitles],
+    mutationFn: (param: Param) =>
+      api.subtitles.modify(param.action, param.form),
+
+    onSuccess: (_, param) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.History],
+      });
+
+      // TODO: Query less
+      const { type, id } = param.form;
+      if (type === "episode") {
+        client.invalidateQueries({
+          queryKey: [QueryKeys.Series, id],
+        });
+      } else {
+        client.invalidateQueries({
+          queryKey: [QueryKeys.Movies, id],
+        });
+      }
     },
-  );
+  });
 }
 
 export function useEpisodeSubtitleModification() {
@@ -36,42 +42,48 @@ export function useEpisodeSubtitleModification() {
     form: T;
   }
 
-  const download = useMutation(
-    [QueryKeys.Subtitles, QueryKeys.Episodes],
-    (param: Param<FormType.Subtitle>) =>
+  const download = useMutation({
+    mutationKey: [QueryKeys.Subtitles, QueryKeys.Episodes],
+
+    mutationFn: (param: Param<FormType.Subtitle>) =>
       api.episodes.downloadSubtitles(
         param.seriesId,
         param.episodeId,
         param.form,
       ),
-    {
-      onSuccess: (_, param) => {
-        client.invalidateQueries([QueryKeys.Series, param.seriesId]);
-      },
+
+    onSuccess: (_, param) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Series, param.seriesId],
+      });
     },
-  );
+  });
 
-  const remove = useMutation(
-    [QueryKeys.Subtitles, QueryKeys.Episodes],
-    (param: Param<FormType.DeleteSubtitle>) =>
+  const remove = useMutation({
+    mutationKey: [QueryKeys.Subtitles, QueryKeys.Episodes],
+
+    mutationFn: (param: Param<FormType.DeleteSubtitle>) =>
       api.episodes.deleteSubtitles(param.seriesId, param.episodeId, param.form),
-    {
-      onSuccess: (_, param) => {
-        client.invalidateQueries([QueryKeys.Series, param.seriesId]);
-      },
+
+    onSuccess: (_, param) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Series, param.seriesId],
+      });
     },
-  );
+  });
 
-  const upload = useMutation(
-    [QueryKeys.Subtitles, QueryKeys.Episodes],
-    (param: Param<FormType.UploadSubtitle>) =>
+  const upload = useMutation({
+    mutationKey: [QueryKeys.Subtitles, QueryKeys.Episodes],
+
+    mutationFn: (param: Param<FormType.UploadSubtitle>) =>
       api.episodes.uploadSubtitles(param.seriesId, param.episodeId, param.form),
-    {
-      onSuccess: (_, { seriesId }) => {
-        client.invalidateQueries([QueryKeys.Series, seriesId]);
-      },
+
+    onSuccess: (_, { seriesId }) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Series, seriesId],
+      });
     },
-  );
+  });
 
   return { download, remove, upload };
 }
@@ -84,46 +96,54 @@ export function useMovieSubtitleModification() {
     form: T;
   }
 
-  const download = useMutation(
-    [QueryKeys.Subtitles, QueryKeys.Movies],
-    (param: Param<FormType.Subtitle>) =>
+  const download = useMutation({
+    mutationKey: [QueryKeys.Subtitles, QueryKeys.Movies],
+
+    mutationFn: (param: Param<FormType.Subtitle>) =>
       api.movies.downloadSubtitles(param.radarrId, param.form),
-    {
-      onSuccess: (_, param) => {
-        client.invalidateQueries([QueryKeys.Movies, param.radarrId]);
-      },
+
+    onSuccess: (_, param) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Movies, param.radarrId],
+      });
     },
-  );
+  });
+
+  const remove = useMutation({
+    mutationKey: [QueryKeys.Subtitles, QueryKeys.Movies],
 
-  const remove = useMutation(
-    [QueryKeys.Subtitles, QueryKeys.Movies],
-    (param: Param<FormType.DeleteSubtitle>) =>
+    mutationFn: (param: Param<FormType.DeleteSubtitle>) =>
       api.movies.deleteSubtitles(param.radarrId, param.form),
-    {
-      onSuccess: (_, param) => {
-        client.invalidateQueries([QueryKeys.Movies, param.radarrId]);
-      },
+
+    onSuccess: (_, param) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Movies, param.radarrId],
+      });
     },
-  );
+  });
 
-  const upload = useMutation(
-    [QueryKeys.Subtitles, QueryKeys.Movies],
-    (param: Param<FormType.UploadSubtitle>) =>
+  const upload = useMutation({
+    mutationKey: [QueryKeys.Subtitles, QueryKeys.Movies],
+
+    mutationFn: (param: Param<FormType.UploadSubtitle>) =>
       api.movies.uploadSubtitles(param.radarrId, param.form),
-    {
-      onSuccess: (_, { radarrId }) => {
-        client.invalidateQueries([QueryKeys.Movies, radarrId]);
-      },
+
+    onSuccess: (_, { radarrId }) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Movies, radarrId],
+      });
     },
-  );
+  });
 
   return { download, remove, upload };
 }
 
 export function useSubtitleInfos(names: string[]) {
-  return useQuery([QueryKeys.Subtitles, QueryKeys.Infos, names], () =>
-    api.subtitles.info(names),
-  );
+  return useQuery({
+    queryKey: [QueryKeys.Subtitles, QueryKeys.Infos, names],
+
+    queryFn: () => api.subtitles.info(names),
+  });
 }
 
 export function useRefTracksByEpisodeId(
@@ -131,11 +151,17 @@ export function useRefTracksByEpisodeId(
   sonarrEpisodeId: number,
   isEpisode: boolean,
 ) {
-  return useQuery(
-    [QueryKeys.Episodes, sonarrEpisodeId, QueryKeys.Subtitles, subtitlesPath],
-    () => api.subtitles.getRefTracksByEpisodeId(subtitlesPath, sonarrEpisodeId),
-    { enabled: isEpisode },
-  );
+  return useQuery({
+    queryKey: [
+      QueryKeys.Episodes,
+      sonarrEpisodeId,
+      QueryKeys.Subtitles,
+      subtitlesPath,
+    ],
+    queryFn: () =>
+      api.subtitles.getRefTracksByEpisodeId(subtitlesPath, sonarrEpisodeId),
+    enabled: isEpisode,
+  });
 }
 
 export function useRefTracksByMovieId(
@@ -143,9 +169,15 @@ export function useRefTracksByMovieId(
   radarrMovieId: number,
   isMovie: boolean,
 ) {
-  return useQuery(
-    [QueryKeys.Movies, radarrMovieId, QueryKeys.Subtitles, subtitlesPath],
-    () => api.subtitles.getRefTracksByMovieId(subtitlesPath, radarrMovieId),
-    { enabled: isMovie },
-  );
+  return useQuery({
+    queryKey: [
+      QueryKeys.Movies,
+      radarrMovieId,
+      QueryKeys.Subtitles,
+      subtitlesPath,
+    ],
+    queryFn: () =>
+      api.subtitles.getRefTracksByMovieId(subtitlesPath, radarrMovieId),
+    enabled: isMovie,
+  });
 }
diff --git a/frontend/src/apis/hooks/system.ts b/frontend/src/apis/hooks/system.ts
index 8b877a977..109e77105 100644
--- a/frontend/src/apis/hooks/system.ts
+++ b/frontend/src/apis/hooks/system.ts
@@ -1,20 +1,18 @@
 import { useMemo } from "react";
-import { useMutation, useQuery, useQueryClient } from "react-query";
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
 import { QueryKeys } from "@/apis/queries/keys";
 import api from "@/apis/raw";
 import { Environment } from "@/utilities";
 import { setAuthenticated } from "@/utilities/event";
 
 export function useBadges() {
-  return useQuery(
-    [QueryKeys.System, QueryKeys.Badges],
-    () => api.badges.all(),
-    {
-      refetchOnWindowFocus: "always",
-      refetchInterval: 1000 * 60,
-      staleTime: 1000 * 10,
-    },
-  );
+  return useQuery({
+    queryKey: [QueryKeys.System, QueryKeys.Badges],
+    queryFn: () => api.badges.all(),
+    refetchOnWindowFocus: "always",
+    refetchInterval: 1000 * 60,
+    staleTime: 1000 * 10,
+  });
 }
 
 export function useFileSystem(
@@ -22,9 +20,10 @@ export function useFileSystem(
   path: string,
   enabled: boolean,
 ) {
-  return useQuery(
-    [QueryKeys.FileSystem, type, path],
-    () => {
+  return useQuery({
+    queryKey: [QueryKeys.FileSystem, type, path],
+
+    queryFn: () => {
       if (type === "bazarr") {
         return api.files.bazarr(path);
       } else if (type === "radarr") {
@@ -32,53 +31,63 @@ export function useFileSystem(
       } else if (type === "sonarr") {
         return api.files.sonarr(path);
       }
+
+      return [];
     },
-    {
-      enabled,
-    },
-  );
+
+    enabled,
+  });
 }
 
 export function useSystemSettings() {
-  return useQuery(
-    [QueryKeys.System, QueryKeys.Settings],
-    () => api.system.settings(),
-    {
-      staleTime: Infinity,
-    },
-  );
+  return useQuery({
+    queryKey: [QueryKeys.System, QueryKeys.Settings],
+    queryFn: () => api.system.settings(),
+    staleTime: Infinity,
+  });
 }
 
 export function useSettingsMutation() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.System, QueryKeys.Settings],
-    (data: LooseObject) => api.system.updateSettings(data),
-    {
-      onSuccess: () => {
-        client.invalidateQueries([QueryKeys.System]);
-        client.invalidateQueries([QueryKeys.Series]);
-        client.invalidateQueries([QueryKeys.Episodes]);
-        client.invalidateQueries([QueryKeys.Movies]);
-        client.invalidateQueries([QueryKeys.Wanted]);
-        client.invalidateQueries([QueryKeys.Badges]);
-      },
+  return useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Settings],
+    mutationFn: (data: LooseObject) => api.system.updateSettings(data),
+
+    onSuccess: () => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.System],
+      });
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Series],
+      });
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Episodes],
+      });
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Movies],
+      });
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Wanted],
+      });
+      client.invalidateQueries({
+        queryKey: [QueryKeys.Badges],
+      });
     },
-  );
+  });
 }
 
 export function useServerSearch(query: string, enabled: boolean) {
-  return useQuery(
-    [QueryKeys.System, QueryKeys.Search, query],
-    () => api.system.search(query),
-    {
-      enabled,
-    },
-  );
+  return useQuery({
+    queryKey: [QueryKeys.System, QueryKeys.Search, query],
+    queryFn: () => api.system.search(query),
+    enabled,
+  });
 }
 
 export function useSystemLogs() {
-  return useQuery([QueryKeys.System, QueryKeys.Logs], () => api.system.logs(), {
+  return useQuery({
+    queryKey: [QueryKeys.System, QueryKeys.Logs],
+    queryFn: () => api.system.logs(),
     refetchOnWindowFocus: "always",
     refetchInterval: 1000 * 60,
     staleTime: 1000 * 10,
@@ -87,171 +96,187 @@ export function useSystemLogs() {
 
 export function useDeleteLogs() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.System, QueryKeys.Logs],
-    () => api.system.deleteLogs(),
-    {
-      onSuccess: () => {
-        client.invalidateQueries([QueryKeys.System, QueryKeys.Logs]);
-      },
+  return useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Logs],
+    mutationFn: () => api.system.deleteLogs(),
+
+    onSuccess: () => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.System, QueryKeys.Logs],
+      });
     },
-  );
+  });
 }
 
 export function useSystemAnnouncements() {
-  return useQuery(
-    [QueryKeys.System, QueryKeys.Announcements],
-    () => api.system.announcements(),
-    {
-      refetchOnWindowFocus: "always",
-      refetchInterval: 1000 * 60,
-      staleTime: 1000 * 10,
-    },
-  );
+  return useQuery({
+    queryKey: [QueryKeys.System, QueryKeys.Announcements],
+    queryFn: () => api.system.announcements(),
+    refetchOnWindowFocus: "always",
+    refetchInterval: 1000 * 60,
+    staleTime: 1000 * 10,
+  });
 }
 
 export function useSystemAnnouncementsAddDismiss() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.System, QueryKeys.Announcements],
-    (param: { hash: string }) => {
+  return useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Announcements],
+
+    mutationFn: (param: { hash: string }) => {
       const { hash } = param;
       return api.system.addAnnouncementsDismiss(hash);
     },
-    {
-      onSuccess: (_, { hash }) => {
-        client.invalidateQueries([QueryKeys.System, QueryKeys.Announcements]);
-        client.invalidateQueries([QueryKeys.System, QueryKeys.Badges]);
-      },
+
+    onSuccess: (_, { hash }) => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.System, QueryKeys.Announcements],
+      });
+      client.invalidateQueries({
+        queryKey: [QueryKeys.System, QueryKeys.Badges],
+      });
     },
-  );
+  });
 }
 
 export function useSystemTasks() {
-  return useQuery(
-    [QueryKeys.System, QueryKeys.Tasks],
-    () => api.system.tasks(),
-    {
-      refetchOnWindowFocus: "always",
-      refetchInterval: 1000 * 60,
-      staleTime: 1000 * 10,
-    },
-  );
+  return useQuery({
+    queryKey: [QueryKeys.System, QueryKeys.Tasks],
+    queryFn: () => api.system.tasks(),
+    refetchOnWindowFocus: "always",
+    refetchInterval: 1000 * 60,
+    staleTime: 1000 * 10,
+  });
 }
 
 export function useRunTask() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.System, QueryKeys.Tasks],
-    (id: string) => api.system.runTask(id),
-    {
-      onSuccess: () => {
-        client.invalidateQueries([QueryKeys.System, QueryKeys.Tasks]);
-        client.invalidateQueries([QueryKeys.System, QueryKeys.Backups]);
-      },
+  return useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Tasks],
+    mutationFn: (id: string) => api.system.runTask(id),
+
+    onSuccess: () => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.System, QueryKeys.Tasks],
+      });
+      client.invalidateQueries({
+        queryKey: [QueryKeys.System, QueryKeys.Backups],
+      });
     },
-  );
+  });
 }
 
 export function useSystemBackups() {
-  return useQuery([QueryKeys.System, "backups"], () => api.system.backups());
+  return useQuery({
+    queryKey: [QueryKeys.System, "backups"],
+    queryFn: () => api.system.backups(),
+  });
 }
 
 export function useCreateBackups() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.System, QueryKeys.Backups],
-    () => api.system.createBackups(),
-    {
-      onSuccess: () => {
-        client.invalidateQueries([QueryKeys.System, QueryKeys.Backups]);
-      },
+  return useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Backups],
+    mutationFn: () => api.system.createBackups(),
+
+    onSuccess: () => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.System, QueryKeys.Backups],
+      });
     },
-  );
+  });
 }
 
 export function useRestoreBackups() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.System, QueryKeys.Backups],
-    (filename: string) => api.system.restoreBackups(filename),
-    {
-      onSuccess: () => {
-        client.invalidateQueries([QueryKeys.System, QueryKeys.Backups]);
-      },
+  return useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Backups],
+    mutationFn: (filename: string) => api.system.restoreBackups(filename),
+
+    onSuccess: () => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.System, QueryKeys.Backups],
+      });
     },
-  );
+  });
 }
 
 export function useDeleteBackups() {
   const client = useQueryClient();
-  return useMutation(
-    [QueryKeys.System, QueryKeys.Backups],
-    (filename: string) => api.system.deleteBackups(filename),
-    {
-      onSuccess: () => {
-        client.invalidateQueries([QueryKeys.System, QueryKeys.Backups]);
-      },
+  return useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Backups],
+    mutationFn: (filename: string) => api.system.deleteBackups(filename),
+
+    onSuccess: () => {
+      client.invalidateQueries({
+        queryKey: [QueryKeys.System, QueryKeys.Backups],
+      });
     },
-  );
+  });
 }
 
 export function useSystemStatus() {
-  return useQuery([QueryKeys.System, "status"], () => api.system.status());
+  return useQuery({
+    queryKey: [QueryKeys.System, "status"],
+    queryFn: () => api.system.status(),
+  });
 }
 
 export function useSystemHealth() {
-  return useQuery([QueryKeys.System, "health"], () => api.system.health());
+  return useQuery({
+    queryKey: [QueryKeys.System, "health"],
+    queryFn: () => api.system.health(),
+  });
 }
 
 export function useSystemReleases() {
-  return useQuery([QueryKeys.System, "releases"], () => api.system.releases());
+  return useQuery({
+    queryKey: [QueryKeys.System, "releases"],
+    queryFn: () => api.system.releases(),
+  });
 }
 
 export function useSystem() {
   const client = useQueryClient();
-  const { mutate: logout, isLoading: isLoggingOut } = useMutation(
-    [QueryKeys.System, QueryKeys.Actions],
-    () => api.system.logout(),
-    {
-      onSuccess: () => {
-        setAuthenticated(false);
-        client.clear();
-      },
+  const { mutate: logout, isPending: isLoggingOut } = useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Actions],
+    mutationFn: () => api.system.logout(),
+
+    onSuccess: () => {
+      setAuthenticated(false);
+      client.clear();
     },
-  );
+  });
 
-  const { mutate: login, isLoading: isLoggingIn } = useMutation(
-    [QueryKeys.System, QueryKeys.Actions],
-    (param: { username: string; password: string }) =>
+  const { mutate: login, isPending: isLoggingIn } = useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Actions],
+
+    mutationFn: (param: { username: string; password: string }) =>
       api.system.login(param.username, param.password),
-    {
-      onSuccess: () => {
-        // TODO: Hard-coded value
-        window.location.replace(Environment.baseUrl);
-      },
+
+    onSuccess: () => {
+      // TODO: Hard-coded value
+      window.location.replace(Environment.baseUrl);
     },
-  );
+  });
 
-  const { mutate: shutdown, isLoading: isShuttingDown } = useMutation(
-    [QueryKeys.System, QueryKeys.Actions],
-    () => api.system.shutdown(),
-    {
-      onSuccess: () => {
-        client.clear();
-      },
+  const { mutate: shutdown, isPending: isShuttingDown } = useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Actions],
+    mutationFn: () => api.system.shutdown(),
+
+    onSuccess: () => {
+      client.clear();
     },
-  );
+  });
+
+  const { mutate: restart, isPending: isRestarting } = useMutation({
+    mutationKey: [QueryKeys.System, QueryKeys.Actions],
+    mutationFn: () => api.system.restart(),
 
-  const { mutate: restart, isLoading: isRestarting } = useMutation(
-    [QueryKeys.System, QueryKeys.Actions],
-    () => api.system.restart(),
-    {
-      onSuccess: () => {
-        client.clear();
-      },
+    onSuccess: () => {
+      client.clear();
     },
-  );
+  });
 
   return useMemo(
     () => ({
diff --git a/frontend/src/apis/queries/hooks.ts b/frontend/src/apis/queries/hooks.ts
index e2e53fdf6..2f17b5efe 100644
--- a/frontend/src/apis/queries/hooks.ts
+++ b/frontend/src/apis/queries/hooks.ts
@@ -4,7 +4,7 @@ import {
   useQuery,
   useQueryClient,
   UseQueryResult,
-} from "react-query";
+} from "@tanstack/react-query";
 import { GetItemId, useOnValueChange } from "@/utilities";
 import { usePageSize } from "@/utilities/storage";
 import { QueryKeys } from "./keys";
@@ -39,31 +39,31 @@ export function usePaginationQuery<
 
   const start = page * pageSize;
 
-  const results = useQuery(
-    [...queryKey, QueryKeys.Range, { start, size: pageSize }],
-    () => {
+  const results = useQuery({
+    queryKey: [...queryKey, QueryKeys.Range, { start, size: pageSize }],
+
+    queryFn: () => {
       const param: Parameter.Range = {
         start,
         length: pageSize,
       };
       return queryFn(param);
     },
-    {
-      onSuccess: ({ data }) => {
-        if (cacheIndividual) {
-          data.forEach((item) => {
-            const id = GetItemId(item);
-            if (id) {
-              client.setQueryData([...queryKey, id], item);
-            }
-          });
-        }
-      },
-    },
-  );
+  });
 
   const { data } = results;
 
+  useEffect(() => {
+    if (results.isSuccess && results.data && cacheIndividual) {
+      results.data.data.forEach((item) => {
+        const id = GetItemId(item);
+        if (id) {
+          client.setQueryData([...queryKey, id], item);
+        }
+      });
+    }
+  }, [results.isSuccess, results.data, client, cacheIndividual, queryKey]);
+
   const totalCount = data?.total ?? 0;
   const pageCount = Math.ceil(totalCount / pageSize);
 
diff --git a/frontend/src/apis/queries/index.ts b/frontend/src/apis/queries/index.ts
index a1a17ffd9..75958f111 100644
--- a/frontend/src/apis/queries/index.ts
+++ b/frontend/src/apis/queries/index.ts
@@ -1,4 +1,4 @@
-import { QueryClient } from "react-query";
+import { QueryClient } from "@tanstack/react-query";
 
 const queryClient = new QueryClient({
   defaultOptions: {
@@ -6,7 +6,11 @@ const queryClient = new QueryClient({
       refetchOnWindowFocus: false,
       retry: false,
       staleTime: 1000 * 60,
-      keepPreviousData: true,
+      networkMode: "offlineFirst",
+      placeholderData: (previousData: object) => previousData,
+    },
+    mutations: {
+      networkMode: "offlineFirst",
     },
   },
 });
diff --git a/frontend/src/components/async/MutateAction.tsx b/frontend/src/components/async/MutateAction.tsx
index 9a5837c02..6fff0dbb7 100644
--- a/frontend/src/components/async/MutateAction.tsx
+++ b/frontend/src/components/async/MutateAction.tsx
@@ -1,5 +1,5 @@
 import { useCallback, useState } from "react";
-import { UseMutationResult } from "react-query";
+import { UseMutationResult } from "@tanstack/react-query";
 import { Action } from "@/components/inputs";
 import { ActionProps } from "@/components/inputs/Action";
 
diff --git a/frontend/src/components/async/MutateButton.tsx b/frontend/src/components/async/MutateButton.tsx
index 5de836278..9197e2d50 100644
--- a/frontend/src/components/async/MutateButton.tsx
+++ b/frontend/src/components/async/MutateButton.tsx
@@ -1,6 +1,6 @@
 import { useCallback, useState } from "react";
-import { UseMutationResult } from "react-query";
 import { Button, ButtonProps } from "@mantine/core";
+import { UseMutationResult } from "@tanstack/react-query";
 
 type MutateButtonProps<DATA, VAR> = Omit<
   ButtonProps,
diff --git a/frontend/src/components/async/QueryOverlay.tsx b/frontend/src/components/async/QueryOverlay.tsx
index f6d0d6572..2a5848cf2 100644
--- a/frontend/src/components/async/QueryOverlay.tsx
+++ b/frontend/src/components/async/QueryOverlay.tsx
@@ -1,6 +1,6 @@
 import { FunctionComponent, ReactNode } from "react";
-import { UseQueryResult } from "react-query";
 import { LoadingOverlay } from "@mantine/core";
+import { UseQueryResult } from "@tanstack/react-query";
 import { LoadingProvider } from "@/contexts";
 
 interface QueryOverlayProps {
diff --git a/frontend/src/components/forms/ItemEditForm.tsx b/frontend/src/components/forms/ItemEditForm.tsx
index a3d03f4c8..392338500 100644
--- a/frontend/src/components/forms/ItemEditForm.tsx
+++ b/frontend/src/components/forms/ItemEditForm.tsx
@@ -1,7 +1,7 @@
 import { FunctionComponent, useMemo } from "react";
-import { UseMutationResult } from "react-query";
 import { Button, Divider, Group, LoadingOverlay, Stack } from "@mantine/core";
 import { useForm } from "@mantine/form";
+import { UseMutationResult } from "@tanstack/react-query";
 import { useLanguageProfiles } from "@/apis/hooks";
 import { MultiSelector, Selector } from "@/components/inputs";
 import { useModals, withModal } from "@/modules/modals";
@@ -21,7 +21,7 @@ const ItemEditForm: FunctionComponent<Props> = ({
   onCancel,
 }) => {
   const { data, isFetching } = useLanguageProfiles();
-  const { isLoading, mutate } = mutation;
+  const { isPending, mutate } = mutation;
   const modals = useModals();
 
   const profileOptions = useSelectorOptions(
@@ -47,7 +47,7 @@ const ItemEditForm: FunctionComponent<Props> = ({
     (v) => v.code2,
   );
 
-  const isOverlayVisible = isLoading || isFetching || item === null;
+  const isOverlayVisible = isPending || isFetching || item === null;
 
   return (
     <form
diff --git a/frontend/src/components/modals/ManualSearchModal.tsx b/frontend/src/components/modals/ManualSearchModal.tsx
index dbacaf989..2c764e83c 100644
--- a/frontend/src/components/modals/ManualSearchModal.tsx
+++ b/frontend/src/components/modals/ManualSearchModal.tsx
@@ -1,5 +1,4 @@
 import { useCallback, useMemo, useState } from "react";
-import { UseQueryResult } from "react-query";
 import { Column } from "react-table";
 import {
   Alert,
@@ -18,6 +17,7 @@ import {
   faInfoCircle,
 } from "@fortawesome/free-solid-svg-icons";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { UseQueryResult } from "@tanstack/react-query";
 import { isString } from "lodash";
 import { Action, PageTable } from "@/components";
 import Language from "@/components/bazarr/Language";
diff --git a/frontend/src/modules/socketio/index.ts b/frontend/src/modules/socketio/index.ts
index 6c0a2810f..67c556480 100644
--- a/frontend/src/modules/socketio/index.ts
+++ b/frontend/src/modules/socketio/index.ts
@@ -1,4 +1,4 @@
-import { onlineManager } from "react-query";
+import { onlineManager } from "@tanstack/react-query";
 import { debounce, forIn, remove, uniq } from "lodash";
 import { io, Socket } from "socket.io-client";
 import { Environment, isDevEnv, isTestEnv } from "@/utilities";
diff --git a/frontend/src/modules/socketio/reducer.ts b/frontend/src/modules/socketio/reducer.ts
index 81157fdc1..d19ff87c4 100644
--- a/frontend/src/modules/socketio/reducer.ts
+++ b/frontend/src/modules/socketio/reducer.ts
@@ -40,13 +40,13 @@ export function createDefaultReducer(): SocketIO.Reducer[] {
       update: (ids) => {
         LOG("info", "Invalidating series", ids);
         ids.forEach((id) => {
-          queryClient.invalidateQueries([QueryKeys.Series, id]);
+          queryClient.invalidateQueries({ queryKey: [QueryKeys.Series, id] });
         });
       },
       delete: (ids) => {
         LOG("info", "Invalidating series", ids);
         ids.forEach((id) => {
-          queryClient.invalidateQueries([QueryKeys.Series, id]);
+          queryClient.invalidateQueries({ queryKey: [QueryKeys.Series, id] });
         });
       },
     },
@@ -55,13 +55,13 @@ export function createDefaultReducer(): SocketIO.Reducer[] {
       update: (ids) => {
         LOG("info", "Invalidating movies", ids);
         ids.forEach((id) => {
-          queryClient.invalidateQueries([QueryKeys.Movies, id]);
+          queryClient.invalidateQueries({ queryKey: [QueryKeys.Movies, id] });
         });
       },
       delete: (ids) => {
         LOG("info", "Invalidating movies", ids);
         ids.forEach((id) => {
-          queryClient.invalidateQueries([QueryKeys.Movies, id]);
+          queryClient.invalidateQueries({ queryKey: [QueryKeys.Movies, id] });
         });
       },
     },
@@ -78,10 +78,9 @@ export function createDefaultReducer(): SocketIO.Reducer[] {
             id,
           ]);
           if (episode !== undefined) {
-            queryClient.invalidateQueries([
-              QueryKeys.Series,
-              episode.sonarrSeriesId,
-            ]);
+            queryClient.invalidateQueries({
+              queryKey: [QueryKeys.Series, episode.sonarrSeriesId],
+            });
           }
         });
       },
@@ -93,10 +92,9 @@ export function createDefaultReducer(): SocketIO.Reducer[] {
             id,
           ]);
           if (episode !== undefined) {
-            queryClient.invalidateQueries([
-              QueryKeys.Series,
-              episode.sonarrSeriesId,
-            ]);
+            queryClient.invalidateQueries({
+              queryKey: [QueryKeys.Series, episode.sonarrSeriesId],
+            });
           }
         });
       },
@@ -105,83 +103,106 @@ export function createDefaultReducer(): SocketIO.Reducer[] {
       key: "episode-wanted",
       update: (ids) => {
         // Find a better way to update wanted
-        queryClient.invalidateQueries([QueryKeys.Episodes, QueryKeys.Wanted]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.Episodes, QueryKeys.Wanted],
+        });
       },
       delete: () => {
-        queryClient.invalidateQueries([QueryKeys.Episodes, QueryKeys.Wanted]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.Episodes, QueryKeys.Wanted],
+        });
       },
     },
     {
       key: "movie-wanted",
       update: (ids) => {
         // Find a better way to update wanted
-        queryClient.invalidateQueries([QueryKeys.Movies, QueryKeys.Wanted]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.Movies, QueryKeys.Wanted],
+        });
       },
       delete: () => {
-        queryClient.invalidateQueries([QueryKeys.Movies, QueryKeys.Wanted]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.Movies, QueryKeys.Wanted],
+        });
       },
     },
     {
       key: "settings",
       any: () => {
-        queryClient.invalidateQueries([QueryKeys.System]);
+        queryClient.invalidateQueries({ queryKey: [QueryKeys.System] });
       },
     },
     {
       key: "languages",
       any: () => {
-        queryClient.invalidateQueries([QueryKeys.System, QueryKeys.Languages]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.System, QueryKeys.Languages],
+        });
       },
     },
     {
       key: "badges",
       any: () => {
-        queryClient.invalidateQueries([QueryKeys.System, QueryKeys.Badges]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.System, QueryKeys.Badges],
+        });
       },
     },
     {
       key: "movie-history",
       any: () => {
-        queryClient.invalidateQueries([QueryKeys.Movies, QueryKeys.History]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.Movies, QueryKeys.History],
+        });
       },
     },
     {
       key: "movie-blacklist",
       any: () => {
-        queryClient.invalidateQueries([QueryKeys.Movies, QueryKeys.Blacklist]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.Movies, QueryKeys.Blacklist],
+        });
       },
     },
     {
       key: "episode-history",
       any: () => {
-        queryClient.invalidateQueries([QueryKeys.Episodes, QueryKeys.History]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.Episodes, QueryKeys.History],
+        });
       },
     },
     {
       key: "episode-blacklist",
       any: () => {
-        queryClient.invalidateQueries([
-          QueryKeys.Episodes,
-          QueryKeys.Blacklist,
-        ]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.Episodes, QueryKeys.Blacklist],
+        });
       },
     },
     {
       key: "reset-episode-wanted",
       any: () => {
-        queryClient.invalidateQueries([QueryKeys.Episodes, QueryKeys.Wanted]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.Episodes, QueryKeys.Wanted],
+        });
       },
     },
     {
       key: "reset-movie-wanted",
       any: () => {
-        queryClient.invalidateQueries([QueryKeys.Movies, QueryKeys.Wanted]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.Movies, QueryKeys.Wanted],
+        });
       },
     },
     {
       key: "task",
       any: () => {
-        queryClient.invalidateQueries([QueryKeys.System, QueryKeys.Tasks]);
+        queryClient.invalidateQueries({
+          queryKey: [QueryKeys.System, QueryKeys.Tasks],
+        });
       },
     },
   ];
diff --git a/frontend/src/pages/Settings/Notifications/components.tsx b/frontend/src/pages/Settings/Notifications/components.tsx
index 7ee5f5a26..8fa17abb2 100644
--- a/frontend/src/pages/Settings/Notifications/components.tsx
+++ b/frontend/src/pages/Settings/Notifications/components.tsx
@@ -1,5 +1,4 @@
 import { FunctionComponent, useCallback, useMemo } from "react";
-import { useMutation } from "react-query";
 import {
   Button,
   Divider,
@@ -9,6 +8,7 @@ import {
   Textarea,
 } from "@mantine/core";
 import { useForm } from "@mantine/form";
+import { useMutation } from "@tanstack/react-query";
 import { isObject } from "lodash";
 import api from "@/apis/raw";
 import { Selector } from "@/components";
@@ -63,7 +63,9 @@ const NotificationForm: FunctionComponent<Props> = ({
     },
   });
 
-  const test = useMutation((url: string) => api.system.testNotification(url));
+  const test = useMutation({
+    mutationFn: (url: string) => api.system.testNotification(url),
+  });
 
   return (
     <form
diff --git a/frontend/src/pages/Settings/components/Layout.tsx b/frontend/src/pages/Settings/components/Layout.tsx
index c2f8c3c35..da72818fa 100644
--- a/frontend/src/pages/Settings/components/Layout.tsx
+++ b/frontend/src/pages/Settings/components/Layout.tsx
@@ -25,7 +25,7 @@ const Layout: FunctionComponent<Props> = (props) => {
   const { children, name } = props;
 
   const { data: settings, isLoading, isRefetching } = useSystemSettings();
-  const { mutate, isLoading: isMutating } = useSettingsMutation();
+  const { mutate, isPending: isMutating } = useSettingsMutation();
 
   const form = useForm<FormValues>({
     initialValues: {
diff --git a/frontend/src/pages/Settings/components/LayoutModal.tsx b/frontend/src/pages/Settings/components/LayoutModal.tsx
index 532f6b6ba..9702ad96e 100644
--- a/frontend/src/pages/Settings/components/LayoutModal.tsx
+++ b/frontend/src/pages/Settings/components/LayoutModal.tsx
@@ -28,7 +28,7 @@ const LayoutModal: FunctionComponent<Props> = (props) => {
   const { children, callbackModal } = props;
 
   const { data: settings, isLoading, isRefetching } = useSystemSettings();
-  const { mutate, isLoading: isMutating } = useSettingsMutation();
+  const { mutate, isPending: isMutating } = useSettingsMutation();
 
   const form = useForm<FormValues>({
     initialValues: {
diff --git a/frontend/src/pages/System/Backups/index.tsx b/frontend/src/pages/System/Backups/index.tsx
index e4e43b425..1057623d1 100644
--- a/frontend/src/pages/System/Backups/index.tsx
+++ b/frontend/src/pages/System/Backups/index.tsx
@@ -10,7 +10,7 @@ import Table from "./table";
 const SystemBackupsView: FunctionComponent = () => {
   const backups = useSystemBackups();
 
-  const { mutate: backup, isLoading: isResetting } = useCreateBackups();
+  const { mutate: backup, isPending: isResetting } = useCreateBackups();
 
   useDocumentTitle("Backups - Bazarr (System)");
 
diff --git a/frontend/src/pages/System/Logs/index.tsx b/frontend/src/pages/System/Logs/index.tsx
index b70389459..cb984a192 100644
--- a/frontend/src/pages/System/Logs/index.tsx
+++ b/frontend/src/pages/System/Logs/index.tsx
@@ -19,7 +19,7 @@ const SystemLogsView: FunctionComponent = () => {
   const logs = useSystemLogs();
   const { isFetching, data, refetch } = logs;
 
-  const { mutate, isLoading } = useDeleteLogs();
+  const { mutate, isPending } = useDeleteLogs();
 
   const download = useCallback(() => {
     window.open(`${Environment.baseUrl}/bazarr.log`);
@@ -98,14 +98,14 @@ const SystemLogsView: FunctionComponent = () => {
               Download
             </Toolbox.Button>
             <Toolbox.Button
-              loading={isLoading}
+              loading={isPending}
               icon={faTrash}
               onClick={() => mutate()}
             >
               Empty
             </Toolbox.Button>
             <Toolbox.Button
-              loading={isLoading}
+              loading={isPending}
               icon={faFilter}
               onClick={openFilterModal}
               rightSection={
diff --git a/frontend/src/pages/System/Providers/index.tsx b/frontend/src/pages/System/Providers/index.tsx
index cd4a52cfd..8b73d53b0 100644
--- a/frontend/src/pages/System/Providers/index.tsx
+++ b/frontend/src/pages/System/Providers/index.tsx
@@ -12,7 +12,7 @@ const SystemProvidersView: FunctionComponent = () => {
 
   const { isFetching, data, refetch } = providers;
 
-  const { mutate: reset, isLoading: isResetting } = useResetProvider();
+  const { mutate: reset, isPending: isResetting } = useResetProvider();
 
   useDocumentTitle("Providers - Bazarr (System)");
 
diff --git a/frontend/src/pages/Wanted/Movies/index.tsx b/frontend/src/pages/Wanted/Movies/index.tsx
index e42b6a6dc..139bf0c46 100644
--- a/frontend/src/pages/Wanted/Movies/index.tsx
+++ b/frontend/src/pages/Wanted/Movies/index.tsx
@@ -42,7 +42,7 @@ const WantedMoviesView: FunctionComponent = () => {
             <Group gap="sm">
               {value.map((item, idx) => (
                 <Badge
-                  color={download.isLoading ? "gray" : undefined}
+                  color={download.isPending ? "gray" : undefined}
                   leftSection={<FontAwesomeIcon icon={faSearch} />}
                   key={BuildKey(idx, item.code2)}
                   style={{ cursor: "pointer" }}
diff --git a/frontend/src/pages/Wanted/Series/index.tsx b/frontend/src/pages/Wanted/Series/index.tsx
index cc9a22f6e..082cc7931 100644
--- a/frontend/src/pages/Wanted/Series/index.tsx
+++ b/frontend/src/pages/Wanted/Series/index.tsx
@@ -50,7 +50,7 @@ const WantedSeriesView: FunctionComponent = () => {
             <Group gap="sm">
               {value.map((item, idx) => (
                 <Badge
-                  color={download.isLoading ? "gray" : undefined}
+                  color={download.isPending ? "gray" : undefined}
                   leftSection={<FontAwesomeIcon icon={faSearch} />}
                   key={BuildKey(idx, item.code2)}
                   style={{ cursor: "pointer" }}
diff --git a/frontend/src/pages/views/MassEditor.tsx b/frontend/src/pages/views/MassEditor.tsx
index e2485b39d..56446e723 100644
--- a/frontend/src/pages/views/MassEditor.tsx
+++ b/frontend/src/pages/views/MassEditor.tsx
@@ -1,9 +1,9 @@
 import { useCallback, useMemo, useState } from "react";
-import { UseMutationResult } from "react-query";
 import { useNavigate } from "react-router-dom";
 import { Column, useRowSelect } from "react-table";
 import { Box, Container } from "@mantine/core";
 import { faCheck, faUndo } from "@fortawesome/free-solid-svg-icons";
+import { UseMutationResult } from "@tanstack/react-query";
 import { uniqBy } from "lodash";
 import { useIsAnyMutationRunning, useLanguageProfiles } from "@/apis/hooks";
 import { SimpleTable, Toolbox } from "@/components";
diff --git a/frontend/src/providers.tsx b/frontend/src/providers.tsx
index 796117529..52a152600 100644
--- a/frontend/src/providers.tsx
+++ b/frontend/src/providers.tsx
@@ -1,7 +1,7 @@
 import { FunctionComponent, PropsWithChildren } from "react";
-import { QueryClientProvider } from "react-query";
-import { ReactQueryDevtools } from "react-query/devtools";
 import { Notifications } from "@mantine/notifications";
+import { QueryClientProvider } from "@tanstack/react-query";
+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
 import queryClient from "@/apis/queries";
 import ThemeProvider from "@/App/ThemeProvider";
 import { ModalsProvider } from "@/modules/modals";
-- 
cgit v1.2.3

