diff --git a/packages/web/src/app/AppProviders.tsx b/packages/web/src/app/AppProviders.tsx
index 3fdfe68e58d..10cd522e412 100644
--- a/packages/web/src/app/AppProviders.tsx
+++ b/packages/web/src/app/AppProviders.tsx
@@ -15,6 +15,7 @@ import {
import { PersistGate } from 'redux-persist/integration/react'
import { WagmiProvider } from 'wagmi'
+import { REACT_QUERY_DEVTOOLS_KEY, useDevToggle } from 'hooks/useDevToggle'
import { useIsMobile } from 'hooks/useIsMobile'
import { env } from 'services/env'
import { queryClient } from 'services/query-client'
@@ -41,6 +42,10 @@ type AppProvidersProps = {
export const AppProviders = ({ children }: AppProvidersProps) => {
const isMobile = useIsMobile()
+ const [reactQueryDevtoolsEnabled] = useDevToggle(
+ REACT_QUERY_DEVTOOLS_KEY,
+ false
+ )
const [{ store, persistor }] = useState(() => {
const theme = getTheme()
@@ -95,7 +100,7 @@ export const AppProviders = ({ children }: AppProvidersProps) => {
-
+ {reactQueryDevtoolsEnabled ? : null}
)
diff --git a/packages/web/src/hooks/useDevToggle.ts b/packages/web/src/hooks/useDevToggle.ts
new file mode 100644
index 00000000000..8f8efceec7d
--- /dev/null
+++ b/packages/web/src/hooks/useDevToggle.ts
@@ -0,0 +1,58 @@
+import { useCallback, useEffect, useState } from 'react'
+
+const CHANGE_EVENT = 'audius:dev-toggle-change'
+
+const read = (key: string, defaultValue: boolean): boolean => {
+ if (typeof window === 'undefined') return defaultValue
+
+ try {
+ const raw = window.localStorage.getItem(key)
+ if (raw === null) return defaultValue
+ return raw === 'true'
+ } catch {
+ return defaultValue
+ }
+}
+
+export const useDevToggle = (
+ key: string,
+ defaultValue: boolean
+): [boolean, (next: boolean) => void] => {
+ const [value, setValue] = useState(() => read(key, defaultValue))
+
+ useEffect(() => {
+ const sync = (event: Event) => {
+ if (event instanceof CustomEvent && event.detail?.key === key) {
+ setValue(read(key, defaultValue))
+ }
+
+ if (event instanceof StorageEvent && event.key === key) {
+ setValue(read(key, defaultValue))
+ }
+ }
+
+ window.addEventListener(CHANGE_EVENT, sync)
+ window.addEventListener('storage', sync)
+
+ return () => {
+ window.removeEventListener(CHANGE_EVENT, sync)
+ window.removeEventListener('storage', sync)
+ }
+ }, [key, defaultValue])
+
+ const set = useCallback(
+ (next: boolean) => {
+ try {
+ window.localStorage.setItem(key, String(next))
+ } catch {}
+
+ setValue(next)
+ window.dispatchEvent(new CustomEvent(CHANGE_EVENT, { detail: { key } }))
+ },
+ [key]
+ )
+
+ return [value, set]
+}
+
+export const REACT_QUERY_DEVTOOLS_KEY = 'audius-react-query-devtools-enabled'
diff --git a/packages/web/src/pages/dev-tools/DevTools.tsx b/packages/web/src/pages/dev-tools/DevTools.tsx
index 2d835a6e28b..0dcc606013a 100644
--- a/packages/web/src/pages/dev-tools/DevTools.tsx
+++ b/packages/web/src/pages/dev-tools/DevTools.tsx
@@ -9,6 +9,7 @@ import {
IconShieldCheck,
IconDashboard,
IconFanClub,
+ IconRefresh,
IconUser,
Paper,
Text,
@@ -19,6 +20,7 @@ import { useNavigate } from 'react-router'
import { Header } from 'components/header/desktop/Header'
import { Page } from 'components/page/Page'
+import { REACT_QUERY_DEVTOOLS_KEY, useDevToggle } from 'hooks/useDevToggle'
import { env } from 'services/env'
import { messages } from './messages'
@@ -102,6 +104,8 @@ export const DevTools = () => {
const dispatch = useDispatch()
const navigate = useNavigate()
const { onOpen: openCoinSuccessModal } = useCoinSuccessModal()
+ const [reactQueryDevtoolsEnabled, setReactQueryDevtoolsEnabled] =
+ useDevToggle(REACT_QUERY_DEVTOOLS_KEY, false)
const handleOpenFeatureFlags = () => {
dispatch(
@@ -217,6 +221,20 @@ export const DevTools = () => {
buttonText={messages.coinSuccessModalPreviewButton}
onButtonClick={handleOpenCoinSuccessModalPreview}
/>
+
+
+ setReactQueryDevtoolsEnabled(!reactQueryDevtoolsEnabled)
+ }
+ />
diff --git a/packages/web/src/pages/dev-tools/messages.ts b/packages/web/src/pages/dev-tools/messages.ts
index 613e61e1841..45524afa157 100644
--- a/packages/web/src/pages/dev-tools/messages.ts
+++ b/packages/web/src/pages/dev-tools/messages.ts
@@ -46,5 +46,10 @@ export const messages = {
coinSuccessModalPreviewTitle: 'Coin success modal',
coinSuccessModalPreviewDescription:
'Open the post-launch fan club success dialog with sample coin data (UI preview only).',
- coinSuccessModalPreviewButton: 'Open coin success modal'
+ coinSuccessModalPreviewButton: 'Open coin success modal',
+ reactQueryDevtoolsTitle: 'React Query Devtools',
+ reactQueryDevtoolsDescription:
+ 'Show the floating React Query devtools button for inspecting queries, mutations, and cache state. Off by default.',
+ reactQueryDevtoolsEnable: 'Show Devtools Button',
+ reactQueryDevtoolsDisable: 'Hide Devtools Button'
}