about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2025-09-09 18:09:10 +0300
committerGitHub <noreply@github.com>2025-09-09 08:09:10 -0700
commit3e2c181c404e2070873bc9c473b428a610bd193e (patch)
tree3a579b8e99852ffa339756e2069850dce4151d09 /src
parentd496a223522b4cdefef0a8725ce31d05e398e68f (diff)
downloadvoidsky-3e2c181c404e2070873bc9c473b428a610bd193e.tar.zst
Upgrade `@types/react` to 19 and run codemod (attempt 2) (#8918)
* update dependencies

* rm `import type React from 'react'`

* run codemods

* patch discord types

* update types/react-dom

* Update yarn.lock
Diffstat (limited to 'src')
-rw-r--r--src/Navigation.tsx2
-rw-r--r--src/alf/typography.tsx1
-rw-r--r--src/components/Button.tsx4
-rw-r--r--src/components/ContextMenu/index.tsx3
-rw-r--r--src/components/ContextMenu/types.ts1
-rw-r--r--src/components/Dialog/types.ts3
-rw-r--r--src/components/Lists.tsx1
-rw-r--r--src/components/Portal.tsx2
-rw-r--r--src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx2
-rw-r--r--src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx1
-rw-r--r--src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/ControlButton.tsx1
-rw-r--r--src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/Scrubber.tsx1
-rw-r--r--src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx10
-rw-r--r--src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx1
-rw-r--r--src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/utils.tsx2
-rw-r--r--src/components/Post/Embed/VideoEmbed/index.web.tsx3
-rw-r--r--src/components/PostControls/PostMenu/index.tsx1
-rw-r--r--src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx1
-rw-r--r--src/components/PostControls/ShareMenu/index.tsx1
-rw-r--r--src/components/ProfileHoverCard/types.ts2
-rw-r--r--src/components/ProgressGuide/FollowDialog.tsx6
-rw-r--r--src/components/ProgressGuide/Toast.tsx4
-rw-r--r--src/components/Select/types.ts2
-rw-r--r--src/components/dialogs/GifSelect.tsx2
-rw-r--r--src/components/dialogs/SearchablePeopleList.tsx2
-rw-r--r--src/components/dms/ActionsWrapper.web.tsx1
-rw-r--r--src/components/forms/InputGroup.tsx7
-rw-r--r--src/components/forms/TextField.tsx4
-rw-r--r--src/components/forms/ToggleButton.tsx9
-rw-r--r--src/components/hooks/useFullscreen.ts2
-rw-r--r--src/components/moderation/LabelPreference.tsx1
-rw-r--r--src/lib/hooks/useAnimatedValue.ts2
-rw-r--r--src/lib/hooks/useOTAUpdates.ts2
-rw-r--r--src/lib/merge-refs.ts2
-rw-r--r--src/screens/Signup/StepCaptcha/CaptchaWebView.tsx2
-rw-r--r--src/screens/Signup/StepInfo/Policies.tsx2
-rw-r--r--src/screens/Signup/StepInfo/index.tsx2
-rw-r--r--src/state/queries/postgate/index.ts2
-rw-r--r--src/state/shell/index.tsx2
-rw-r--r--src/view/com/composer/Composer.tsx2
-rw-r--r--src/view/com/composer/photos/EditImageDialog.tsx2
-rw-r--r--src/view/com/composer/photos/EditImageDialog.web.tsx2
-rw-r--r--src/view/com/feeds/FeedPage.tsx9
-rw-r--r--src/view/com/home/HomeHeaderLayout.web.tsx3
-rw-r--r--src/view/com/home/HomeHeaderLayoutMobile.tsx1
-rw-r--r--src/view/com/lists/ListMembers.tsx2
-rw-r--r--src/view/com/lists/MyLists.tsx10
-rw-r--r--src/view/com/notifications/NotificationFeedItem.tsx2
-rw-r--r--src/view/com/pager/Pager.tsx1
-rw-r--r--src/view/com/pager/Pager.web.tsx1
-rw-r--r--src/view/com/pager/PagerWithHeader.tsx2
-rw-r--r--src/view/com/pager/PagerWithHeader.web.tsx11
-rw-r--r--src/view/com/pager/TabBar.web.tsx4
-rw-r--r--src/view/com/posts/PostFeed.tsx10
-rw-r--r--src/view/com/util/EventStopper.tsx1
-rw-r--r--src/view/com/util/Link.tsx2
-rw-r--r--src/view/com/util/List.tsx2
-rw-r--r--src/view/com/util/List.web.tsx19
-rw-r--r--src/view/com/util/PostMeta.tsx1
-rw-r--r--src/view/com/util/TimeElapsed.tsx4
-rw-r--r--src/view/com/util/ViewHeader.tsx2
-rw-r--r--src/view/com/util/ViewSelector.tsx8
-rw-r--r--src/view/com/util/fab/FABInner.tsx4
-rw-r--r--src/view/com/util/forms/NativeDropdown.web.tsx2
-rw-r--r--src/view/com/util/images/Gallery.tsx1
-rw-r--r--src/view/screens/Storybook/Dialogs.tsx2
-rw-r--r--src/view/shell/Drawer.tsx2
-rw-r--r--src/view/shell/bottom-bar/BottomBar.tsx2
-rw-r--r--src/view/shell/bottom-bar/BottomBarWeb.tsx2
-rw-r--r--src/view/shell/desktop/LeftNav.tsx2
70 files changed, 119 insertions, 98 deletions
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index cdc0fc220..003f9c2e8 100644
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -1,4 +1,4 @@
-import {useCallback, useRef} from 'react'
+import {type JSX, useCallback, useRef} from 'react'
 import {Linking} from 'react-native'
 import * as Notifications from 'expo-notifications'
 import {i18n, type MessageDescriptor} from '@lingui/core'
diff --git a/src/alf/typography.tsx b/src/alf/typography.tsx
index a3bed4e4c..c229ab443 100644
--- a/src/alf/typography.tsx
+++ b/src/alf/typography.tsx
@@ -3,7 +3,6 @@ import {type TextProps as RNTextProps} from 'react-native'
 import {type StyleProp, type TextStyle} from 'react-native'
 import {UITextView} from 'react-native-uitextview'
 import createEmojiRegex from 'emoji-regex'
-import type React from 'react'
 
 import {isNative} from '#/platform/detection'
 import {isIOS} from '#/platform/detection'
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 5a0f0c1c7..af5641ab1 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -71,8 +71,8 @@ export type ButtonState = {
 export type ButtonContext = VariantProps & ButtonState
 
 type NonTextElements =
-  | React.ReactElement
-  | Iterable<React.ReactElement | null | undefined | boolean>
+  | React.ReactElement<any>
+  | Iterable<React.ReactElement<any> | null | undefined | boolean>
 
 export type ButtonProps = Pick<
   PressableProps,
diff --git a/src/components/ContextMenu/index.tsx b/src/components/ContextMenu/index.tsx
index dd7be13d0..9d7189473 100644
--- a/src/components/ContextMenu/index.tsx
+++ b/src/components/ContextMenu/index.tsx
@@ -119,7 +119,8 @@ export function Root({children}: {children: React.ReactNode}) {
   const hoverablesSV = useSharedValue<
     Record<string, {id: string; rect: Measurement}>
   >({})
-  const syncHoverablesThrottleRef = useRef<ReturnType<typeof setTimeout>>()
+  const syncHoverablesThrottleRef =
+    useRef<ReturnType<typeof setTimeout>>(undefined)
   const [hoveredMenuItem, setHoveredMenuItem] = useState<string | null>(null)
 
   const onHoverableTouchUp = useCallback((id: string) => {
diff --git a/src/components/ContextMenu/types.ts b/src/components/ContextMenu/types.ts
index 265a746ca..13f365e53 100644
--- a/src/components/ContextMenu/types.ts
+++ b/src/components/ContextMenu/types.ts
@@ -5,7 +5,6 @@ import {
   type ViewStyle,
 } from 'react-native'
 import {type SharedValue} from 'react-native-reanimated'
-import type React from 'react'
 
 import type * as Dialog from '#/components/Dialog'
 import {
diff --git a/src/components/Dialog/types.ts b/src/components/Dialog/types.ts
index 1308e625c..938d7c744 100644
--- a/src/components/Dialog/types.ts
+++ b/src/components/Dialog/types.ts
@@ -5,7 +5,6 @@ import {
   type StyleProp,
   type ViewStyle,
 } from 'react-native'
-import type React from 'react'
 
 import {type ViewStyleProp} from '#/alf'
 import {type BottomSheetViewProps} from '../../../modules/bottom-sheet'
@@ -34,7 +33,7 @@ export type DialogControlRefProps = {
  */
 export type DialogControlProps = DialogControlRefProps & {
   id: string
-  ref: React.RefObject<DialogControlRefProps>
+  ref: React.RefObject<DialogControlRefProps | null>
   isOpen?: boolean
 }
 
diff --git a/src/components/Lists.tsx b/src/components/Lists.tsx
index 311df3bcb..97cb4e710 100644
--- a/src/components/Lists.tsx
+++ b/src/components/Lists.tsx
@@ -2,7 +2,6 @@ import {memo} from 'react'
 import {type StyleProp, View, type ViewStyle} from 'react-native'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import type React from 'react'
 
 import {cleanError} from '#/lib/strings/errors'
 import {CenteredView} from '#/view/com/util/Views'
diff --git a/src/components/Portal.tsx b/src/components/Portal.tsx
index b4bebce4d..bbfc8b359 100644
--- a/src/components/Portal.tsx
+++ b/src/components/Portal.tsx
@@ -10,7 +10,7 @@ import {
   useState,
 } from 'react'
 
-type Component = React.ReactElement
+type Component = React.ReactElement<any>
 
 type ContextType = {
   outlet: Component | null
diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx
index 266438c04..52449698c 100644
--- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx
+++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx
@@ -139,7 +139,7 @@ function useHLS({
   playlist: string
   setHasSubtitleTrack: (v: boolean) => void
   setError: (v: Error | null) => void
-  videoRef: React.RefObject<HTMLVideoElement>
+  videoRef: React.RefObject<HTMLVideoElement | null>
   setHlsLoading: (v: boolean) => void
 }) {
   const [Hls, setHls] = useState<typeof HlsTypes.default | undefined>(
diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx
index 37b44751d..095136944 100644
--- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx
+++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx
@@ -1,7 +1,6 @@
 import {View} from 'react-native'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import type React from 'react'
 
 import {atoms as a, useTheme} from '#/alf'
 import {Button, ButtonText} from '#/components/Button'
diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/ControlButton.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/ControlButton.tsx
index 9b0c963ea..5178b9283 100644
--- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/ControlButton.tsx
+++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/ControlButton.tsx
@@ -1,5 +1,4 @@
 import {type SvgProps} from 'react-native-svg'
-import type React from 'react'
 
 import {PressableWithHover} from '#/view/com/util/PressableWithHover'
 import {atoms as a, useTheme, web} from '#/alf'
diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/Scrubber.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/Scrubber.tsx
index e4814462f..39661adef 100644
--- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/Scrubber.tsx
+++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/Scrubber.tsx
@@ -2,7 +2,6 @@ import {useCallback, useEffect, useRef, useState} from 'react'
 import {View} from 'react-native'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import type React from 'react'
 
 import {isFirefox, isTouchDevice} from '#/lib/browser'
 import {clamp} from '#/lib/numbers'
diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx
index 7a54ef486..78b457ed6 100644
--- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx
+++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx
@@ -46,14 +46,14 @@ export function Controls({
   hlsLoading,
   hasSubtitleTrack,
 }: {
-  videoRef: React.RefObject<HTMLVideoElement>
-  hlsRef: React.RefObject<Hls | undefined>
+  videoRef: React.RefObject<HTMLVideoElement | null>
+  hlsRef: React.RefObject<Hls | undefined | null>
   active: boolean
   setActive: () => void
   focused: boolean
   setFocused: (focused: boolean) => void
   onScreen: boolean
-  fullscreenRef: React.RefObject<HTMLDivElement>
+  fullscreenRef: React.RefObject<HTMLDivElement | null>
   hlsLoading: boolean
   hasSubtitleTrack: boolean
 }) {
@@ -232,7 +232,7 @@ export function Controls({
   }, [onSeek, videoRef])
 
   const [showCursor, setShowCursor] = useState(true)
-  const cursorTimeoutRef = useRef<ReturnType<typeof setTimeout>>()
+  const cursorTimeoutRef = useRef<ReturnType<typeof setTimeout>>(undefined)
   const onPointerMoveEmptySpace = useCallback(() => {
     setShowCursor(true)
     if (cursorTimeoutRef.current) {
@@ -264,7 +264,7 @@ export function Controls({
     [hovered],
   )
 
-  const timeoutRef = useRef<ReturnType<typeof setTimeout>>()
+  const timeoutRef = useRef<ReturnType<typeof setTimeout>>(undefined)
 
   const onHoverWithTimeout = useCallback(() => {
     onHover()
diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx
index ec5f23fc0..4bb66e17a 100644
--- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx
+++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VolumeControl.tsx
@@ -3,7 +3,6 @@ import {View} from 'react-native'
 import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import type React from 'react'
 
 import {isSafari, isTouchDevice} from '#/lib/browser'
 import {atoms as a} from '#/alf'
diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/utils.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/utils.tsx
index db5ae6ac3..c2ebdca51 100644
--- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/utils.tsx
+++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/utils.tsx
@@ -4,7 +4,7 @@ import {isSafari} from '#/lib/browser'
 import {logger} from '#/logger'
 import {useVideoVolumeState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext'
 
-export function useVideoElement(ref: RefObject<HTMLVideoElement>) {
+export function useVideoElement(ref: RefObject<HTMLVideoElement | null>) {
   const [playing, setPlaying] = useState(false)
   const [muted, setMuted] = useState(true)
   const [currentTime, setCurrentTime] = useState(0)
diff --git a/src/components/Post/Embed/VideoEmbed/index.web.tsx b/src/components/Post/Embed/VideoEmbed/index.web.tsx
index 28341d826..25f9f4604 100644
--- a/src/components/Post/Embed/VideoEmbed/index.web.tsx
+++ b/src/components/Post/Embed/VideoEmbed/index.web.tsx
@@ -10,7 +10,6 @@ import {View} from 'react-native'
 import {type AppBskyEmbedVideo} from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import type React from 'react'
 
 import {isFirefox} from '#/lib/browser'
 import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
@@ -38,7 +37,7 @@ export function VideoEmbed({
     useActiveVideoWeb()
   const [onScreen, setOnScreen] = useState(false)
   const [isFullscreen] = useFullscreen()
-  const lastKnownTime = useRef<number | undefined>()
+  const lastKnownTime = useRef<number | undefined>(undefined)
 
   useEffect(() => {
     if (!ref.current) return
diff --git a/src/components/PostControls/PostMenu/index.tsx b/src/components/PostControls/PostMenu/index.tsx
index 1102aa9a4..950bc4f6d 100644
--- a/src/components/PostControls/PostMenu/index.tsx
+++ b/src/components/PostControls/PostMenu/index.tsx
@@ -8,7 +8,6 @@ import {
 } from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import type React from 'react'
 
 import {type Shadow} from '#/state/cache/post-shadow'
 import {EventStopper} from '#/view/com/util/EventStopper'
diff --git a/src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx b/src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx
index d074cdcf0..ac424c37a 100644
--- a/src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx
+++ b/src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx
@@ -3,7 +3,6 @@ import {AtUri} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useNavigation} from '@react-navigation/native'
-import type React from 'react'
 
 import {makeProfileLink} from '#/lib/routes/links'
 import {type NavigationProp} from '#/lib/routes/types'
diff --git a/src/components/PostControls/ShareMenu/index.tsx b/src/components/PostControls/ShareMenu/index.tsx
index 6f59c0d42..6127ca41d 100644
--- a/src/components/PostControls/ShareMenu/index.tsx
+++ b/src/components/PostControls/ShareMenu/index.tsx
@@ -9,7 +9,6 @@ import {
 } from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import type React from 'react'
 
 import {makeProfileLink} from '#/lib/routes/links'
 import {shareUrl} from '#/lib/sharing'
diff --git a/src/components/ProfileHoverCard/types.ts b/src/components/ProfileHoverCard/types.ts
index f99254e40..53513c695 100644
--- a/src/components/ProfileHoverCard/types.ts
+++ b/src/components/ProfileHoverCard/types.ts
@@ -1,5 +1,3 @@
-import type React from 'react'
-
 import {type ViewStyleProp} from '#/alf'
 
 export type ProfileHoverCardProps = ViewStyleProp & {
diff --git a/src/components/ProgressGuide/FollowDialog.tsx b/src/components/ProgressGuide/FollowDialog.tsx
index f2eb4fa3d..c4a5f0fa0 100644
--- a/src/components/ProgressGuide/FollowDialog.tsx
+++ b/src/components/ProgressGuide/FollowDialog.tsx
@@ -293,8 +293,8 @@ let Header = ({
   interestsDisplayNames,
 }: {
   guide: Follow10ProgressGuide
-  inputRef: React.RefObject<TextInput>
-  listRef: React.RefObject<ListMethods>
+  inputRef: React.RefObject<TextInput | null>
+  listRef: React.RefObject<ListMethods | null>
   onSelectTab: (v: string) => void
   searchText: string
   setHeaderHeight: (v: number) => void
@@ -565,7 +565,7 @@ function SearchInput({
 }: {
   onChangeText: (text: string) => void
   onEscape: () => void
-  inputRef: React.RefObject<TextInput>
+  inputRef: React.RefObject<TextInput | null>
   defaultValue: string
 }) {
   const t = useTheme()
diff --git a/src/components/ProgressGuide/Toast.tsx b/src/components/ProgressGuide/Toast.tsx
index b26c718f8..d4ac771b2 100644
--- a/src/components/ProgressGuide/Toast.tsx
+++ b/src/components/ProgressGuide/Toast.tsx
@@ -14,7 +14,7 @@ import {useLingui} from '@lingui/react'
 import {isWeb} from '#/platform/detection'
 import {atoms as a, useTheme} from '#/alf'
 import {Portal} from '#/components/Portal'
-import {AnimatedCheck, AnimatedCheckRef} from '../anim/AnimatedCheck'
+import {AnimatedCheck, type AnimatedCheckRef} from '../anim/AnimatedCheck'
 import {Text} from '../Typography'
 
 export interface ProgressGuideToastRef {
@@ -39,7 +39,7 @@ export const ProgressGuideToast = React.forwardRef<
   const translateY = useSharedValue(0)
   const opacity = useSharedValue(0)
   const animatedCheckRef = React.useRef<AnimatedCheckRef | null>(null)
-  const timeoutRef = React.useRef<NodeJS.Timeout | undefined>()
+  const timeoutRef = React.useRef<NodeJS.Timeout | undefined>(undefined)
   const winDim = useWindowDimensions()
 
   /**
diff --git a/src/components/Select/types.ts b/src/components/Select/types.ts
index 661621a60..0e58c197b 100644
--- a/src/components/Select/types.ts
+++ b/src/components/Select/types.ts
@@ -160,7 +160,7 @@ export type ContentProps<T> = {
     item: T,
     index: number,
     selectedValue?: string | null,
-  ) => React.ReactElement
+  ) => React.ReactElement<any>
   /*
    * Extracts the value from an item. Defaults to `item => item.value`
    */
diff --git a/src/components/dialogs/GifSelect.tsx b/src/components/dialogs/GifSelect.tsx
index fbbc70f11..d26cec2c6 100644
--- a/src/components/dialogs/GifSelect.tsx
+++ b/src/components/dialogs/GifSelect.tsx
@@ -37,7 +37,7 @@ export function GifSelectDialog({
   onClose,
   onSelectGif: onSelectGifProp,
 }: {
-  controlRef: React.RefObject<{open: () => void}>
+  controlRef: React.RefObject<{open: () => void} | null>
   onClose?: () => void
   onSelectGif: (gif: Gif) => void
 }) {
diff --git a/src/components/dialogs/SearchablePeopleList.tsx b/src/components/dialogs/SearchablePeopleList.tsx
index 159f623b9..4259f3760 100644
--- a/src/components/dialogs/SearchablePeopleList.tsx
+++ b/src/components/dialogs/SearchablePeopleList.tsx
@@ -484,7 +484,7 @@ function SearchInput({
   value: string
   onChangeText: (text: string) => void
   onEscape: () => void
-  inputRef: React.RefObject<TextInput>
+  inputRef: React.RefObject<TextInput | null>
 }) {
   const t = useTheme()
   const {_} = useLingui()
diff --git a/src/components/dms/ActionsWrapper.web.tsx b/src/components/dms/ActionsWrapper.web.tsx
index 75a9b5278..f45aa4ab9 100644
--- a/src/components/dms/ActionsWrapper.web.tsx
+++ b/src/components/dms/ActionsWrapper.web.tsx
@@ -3,7 +3,6 @@ import {Pressable, View} from 'react-native'
 import {type ChatBskyConvoDefs} from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import type React from 'react'
 
 import {useConvoActive} from '#/state/messages/convo'
 import {useSession} from '#/state/session'
diff --git a/src/components/forms/InputGroup.tsx b/src/components/forms/InputGroup.tsx
index 6908d4df8..aacdc60c7 100644
--- a/src/components/forms/InputGroup.tsx
+++ b/src/components/forms/InputGroup.tsx
@@ -23,9 +23,12 @@ export function InputGroup(props: React.PropsWithChildren<{}>) {
             {React.cloneElement(child, {
               // @ts-ignore
               style: [
+                // @ts-ignore
                 ...(Array.isArray(child.props?.style)
-                  ? child.props.style
-                  : [child.props.style || {}]),
+                  ? // @ts-ignore
+                    child.props.style
+                  : // @ts-ignore
+                    [child.props.style || {}]),
                 {
                   borderTopLeftRadius: i > 0 ? 0 : undefined,
                   borderTopRightRadius: i > 0 ? 0 : undefined,
diff --git a/src/components/forms/TextField.tsx b/src/components/forms/TextField.tsx
index 3d4caa93b..85fb7c481 100644
--- a/src/components/forms/TextField.tsx
+++ b/src/components/forms/TextField.tsx
@@ -28,7 +28,7 @@ import {type Props as SVGIconProps} from '#/components/icons/common'
 import {Text} from '#/components/Typography'
 
 const Context = createContext<{
-  inputRef: React.RefObject<TextInput> | null
+  inputRef: React.RefObject<TextInput | null> | null
   isInvalid: boolean
   hovered: boolean
   onHoverIn: () => void
@@ -152,7 +152,7 @@ export type InputProps = Omit<TextInputProps, 'value' | 'onChangeText'> & {
   value?: string
   onChangeText?: (value: string) => void
   isInvalid?: boolean
-  inputRef?: React.RefObject<TextInput> | React.ForwardedRef<TextInput>
+  inputRef?: React.RefObject<TextInput | null> | React.ForwardedRef<TextInput>
 }
 
 export function createInput(Component: typeof TextInput) {
diff --git a/src/components/forms/ToggleButton.tsx b/src/components/forms/ToggleButton.tsx
index 8e08665fd..fab9414f5 100644
--- a/src/components/forms/ToggleButton.tsx
+++ b/src/components/forms/ToggleButton.tsx
@@ -1,5 +1,10 @@
 import React from 'react'
-import {AccessibilityProps, TextStyle, View, ViewStyle} from 'react-native'
+import {
+  type AccessibilityProps,
+  type TextStyle,
+  View,
+  type ViewStyle,
+} from 'react-native'
 
 import {atoms as a, native, useTheme} from '#/alf'
 import * as Toggle from '#/components/forms/Toggle'
@@ -7,7 +12,7 @@ import {Text} from '#/components/Typography'
 
 type ItemProps = Omit<Toggle.ItemProps, 'style' | 'role' | 'children'> &
   AccessibilityProps & {
-    children: React.ReactElement
+    children: React.ReactElement<any>
     testID?: string
   }
 
diff --git a/src/components/hooks/useFullscreen.ts b/src/components/hooks/useFullscreen.ts
index 498f22223..cbe3e581c 100644
--- a/src/components/hooks/useFullscreen.ts
+++ b/src/components/hooks/useFullscreen.ts
@@ -14,7 +14,7 @@ function fullscreenSubscribe(onChange: () => void) {
   return () => document.removeEventListener('fullscreenchange', onChange)
 }
 
-export function useFullscreen(ref?: React.RefObject<HTMLElement>) {
+export function useFullscreen(ref?: React.RefObject<HTMLElement | null>) {
   if (!isWeb) throw new Error("'useFullscreen' is a web-only hook")
   const isFullscreen = useSyncExternalStore(fullscreenSubscribe, () =>
     Boolean(document.fullscreenElement),
diff --git a/src/components/moderation/LabelPreference.tsx b/src/components/moderation/LabelPreference.tsx
index edbb12d0c..f477792d1 100644
--- a/src/components/moderation/LabelPreference.tsx
+++ b/src/components/moderation/LabelPreference.tsx
@@ -5,7 +5,6 @@ import {
 } from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import type React from 'react'
 
 import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings'
 import {useLabelBehaviorDescription} from '#/lib/moderation/useLabelBehaviorDescription'
diff --git a/src/lib/hooks/useAnimatedValue.ts b/src/lib/hooks/useAnimatedValue.ts
index 1307ef952..9ae14dab6 100644
--- a/src/lib/hooks/useAnimatedValue.ts
+++ b/src/lib/hooks/useAnimatedValue.ts
@@ -2,7 +2,7 @@ import * as React from 'react'
 import {Animated} from 'react-native'
 
 export function useAnimatedValue(initialValue: number) {
-  const lazyRef = React.useRef<Animated.Value>()
+  const lazyRef = React.useRef<Animated.Value>(undefined)
 
   if (lazyRef.current === undefined) {
     lazyRef.current = new Animated.Value(initialValue)
diff --git a/src/lib/hooks/useOTAUpdates.ts b/src/lib/hooks/useOTAUpdates.ts
index ba46b6055..9c5750606 100644
--- a/src/lib/hooks/useOTAUpdates.ts
+++ b/src/lib/hooks/useOTAUpdates.ts
@@ -127,7 +127,7 @@ export function useOTAUpdates() {
   const appState = React.useRef<AppStateStatus>('active')
   const lastMinimize = React.useRef(0)
   const ranInitialCheck = React.useRef(false)
-  const timeout = React.useRef<NodeJS.Timeout>()
+  const timeout = React.useRef<NodeJS.Timeout>(undefined)
   const {currentlyRunning, isUpdatePending} = useUpdates()
   const currentChannel = currentlyRunning?.channel
 
diff --git a/src/lib/merge-refs.ts b/src/lib/merge-refs.ts
index 4617b5260..b03899547 100644
--- a/src/lib/merge-refs.ts
+++ b/src/lib/merge-refs.ts
@@ -13,7 +13,7 @@
  * returns a ref callback function that can be used to merge multiple refs into a single ref.
  */
 export function mergeRefs<T = any>(
-  refs: Array<React.MutableRefObject<T> | React.LegacyRef<T>>,
+  refs: Array<React.MutableRefObject<T> | React.Ref<T>>,
 ): React.RefCallback<T> {
   return value => {
     refs.forEach(ref => {
diff --git a/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx b/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx
index 27951d4cc..f208951d5 100644
--- a/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx
+++ b/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx
@@ -31,7 +31,7 @@ export function CaptchaWebView({
   onError: (error: unknown) => void
 }) {
   const startedAt = useRef(Date.now())
-  const successTo = useRef<NodeJS.Timeout>()
+  const successTo = useRef<NodeJS.Timeout>(undefined)
 
   useEffect(() => {
     return () => {
diff --git a/src/screens/Signup/StepInfo/Policies.tsx b/src/screens/Signup/StepInfo/Policies.tsx
index 36de08f6e..754f15e7e 100644
--- a/src/screens/Signup/StepInfo/Policies.tsx
+++ b/src/screens/Signup/StepInfo/Policies.tsx
@@ -72,7 +72,7 @@ export const Policies = ({
     )
   }
 
-  let els: ReactElement
+  let els: ReactElement<any>
   if (tos && pp) {
     els = (
       <Trans>
diff --git a/src/screens/Signup/StepInfo/index.tsx b/src/screens/Signup/StepInfo/index.tsx
index b7db3fb98..842ddadc5 100644
--- a/src/screens/Signup/StepInfo/index.tsx
+++ b/src/screens/Signup/StepInfo/index.tsx
@@ -60,7 +60,7 @@ export function StepInfo({
 
   const [hasWarnedEmail, setHasWarnedEmail] = React.useState<boolean>(false)
 
-  const tldtsRef = React.useRef<typeof tldts>()
+  const tldtsRef = React.useRef<typeof tldts>(undefined)
   React.useEffect(() => {
     // @ts-expect-error - valid path
     import('tldts/dist/index.cjs.min.js').then(tldts => {
diff --git a/src/state/queries/postgate/index.ts b/src/state/queries/postgate/index.ts
index 8c86dc017..95eb94f8e 100644
--- a/src/state/queries/postgate/index.ts
+++ b/src/state/queries/postgate/index.ts
@@ -173,7 +173,7 @@ export function useToggleQuoteDetachmentMutation() {
   const agent = useAgent()
   const queryClient = useQueryClient()
   const getPosts = useGetPosts()
-  const prevEmbed = React.useRef<AppBskyFeedDefs.PostView['embed']>()
+  const prevEmbed = React.useRef<AppBskyFeedDefs.PostView['embed']>(undefined)
 
   return useMutation({
     mutationFn: async ({
diff --git a/src/state/shell/index.tsx b/src/state/shell/index.tsx
index 92cf520a3..809a521bf 100644
--- a/src/state/shell/index.tsx
+++ b/src/state/shell/index.tsx
@@ -1,5 +1,3 @@
-import type React from 'react'
-
 import {Provider as ColorModeProvider} from './color-mode'
 import {Provider as DrawerOpenProvider} from './drawer-open'
 import {Provider as DrawerSwipableProvider} from './drawer-swipe-disabled'
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index 61715a988..2a9635402 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -174,7 +174,7 @@ export const ComposePost = ({
   videoUri: initVideoUri,
   cancelRef,
 }: Props & {
-  cancelRef?: React.RefObject<CancelRef>
+  cancelRef?: React.RefObject<CancelRef | null>
 }) => {
   const {currentAccount} = useSession()
   const agent = useAgent()
diff --git a/src/view/com/composer/photos/EditImageDialog.tsx b/src/view/com/composer/photos/EditImageDialog.tsx
index 9799f7b82..9a0eb9084 100644
--- a/src/view/com/composer/photos/EditImageDialog.tsx
+++ b/src/view/com/composer/photos/EditImageDialog.tsx
@@ -1,5 +1,3 @@
-import type React from 'react'
-
 import {type ComposerImage} from '#/state/gallery'
 import type * as Dialog from '#/components/Dialog'
 
diff --git a/src/view/com/composer/photos/EditImageDialog.web.tsx b/src/view/com/composer/photos/EditImageDialog.web.tsx
index 9a170736a..cda4e9ecf 100644
--- a/src/view/com/composer/photos/EditImageDialog.web.tsx
+++ b/src/view/com/composer/photos/EditImageDialog.web.tsx
@@ -112,7 +112,7 @@ function EditImageInner({
   aspectRatio,
 }: Required<Pick<EditImageDialogProps, 'image'>> &
   Omit<EditImageDialogProps, 'control' | 'image'> & {
-    saveRef: React.RefObject<{save: () => Promise<void>}>
+    saveRef: React.RefObject<{save: () => Promise<void>} | null>
   }) {
   const t = useTheme()
   const [isDragging, setIsDragging] = useState(false)
diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx
index 0f93e66e3..219212232 100644
--- a/src/view/com/feeds/FeedPage.tsx
+++ b/src/view/com/feeds/FeedPage.tsx
@@ -1,4 +1,11 @@
-import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
+import {
+  type JSX,
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from 'react'
 import {View} from 'react-native'
 import {type AppBskyActorDefs, AppBskyFeedDefs} from '@atproto/api'
 import {msg} from '@lingui/macro'
diff --git a/src/view/com/home/HomeHeaderLayout.web.tsx b/src/view/com/home/HomeHeaderLayout.web.tsx
index d8bbe19e6..b12ab69b7 100644
--- a/src/view/com/home/HomeHeaderLayout.web.tsx
+++ b/src/view/com/home/HomeHeaderLayout.web.tsx
@@ -1,7 +1,8 @@
-import React from 'react'
+import {type JSX} from 'react'
 import {View} from 'react-native'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
+import type React from 'react'
 
 import {useKawaiiMode} from '#/state/preferences/kawaii'
 import {useSession} from '#/state/session'
diff --git a/src/view/com/home/HomeHeaderLayoutMobile.tsx b/src/view/com/home/HomeHeaderLayoutMobile.tsx
index 7a40604f4..a0d7b3e78 100644
--- a/src/view/com/home/HomeHeaderLayoutMobile.tsx
+++ b/src/view/com/home/HomeHeaderLayoutMobile.tsx
@@ -1,3 +1,4 @@
+import {type JSX} from 'react'
 import {View} from 'react-native'
 import Animated from 'react-native-reanimated'
 import {msg} from '@lingui/macro'
diff --git a/src/view/com/lists/ListMembers.tsx b/src/view/com/lists/ListMembers.tsx
index 541c8abf9..0771dcd4b 100644
--- a/src/view/com/lists/ListMembers.tsx
+++ b/src/view/com/lists/ListMembers.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback} from 'react'
+import React, {type JSX, useCallback} from 'react'
 import {
   Dimensions,
   type GestureResponderEvent,
diff --git a/src/view/com/lists/MyLists.tsx b/src/view/com/lists/MyLists.tsx
index 5f94946e8..18110b8ba 100644
--- a/src/view/com/lists/MyLists.tsx
+++ b/src/view/com/lists/MyLists.tsx
@@ -1,13 +1,13 @@
-import React from 'react'
+import React, {type JSX} from 'react'
 import {
   ActivityIndicator,
   FlatList as RNFlatList,
   RefreshControl,
-  StyleProp,
+  type StyleProp,
   View,
-  ViewStyle,
+  type ViewStyle,
 } from 'react-native'
-import {AppBskyGraphDefs as GraphDefs} from '@atproto/api'
+import {type AppBskyGraphDefs as GraphDefs} from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
@@ -16,7 +16,7 @@ import {cleanError} from '#/lib/strings/errors'
 import {s} from '#/lib/styles'
 import {logger} from '#/logger'
 import {useModerationOpts} from '#/state/preferences/moderation-opts'
-import {MyListsFilter, useMyListsQuery} from '#/state/queries/my-lists'
+import {type MyListsFilter, useMyListsQuery} from '#/state/queries/my-lists'
 import {atoms as a, useTheme} from '#/alf'
 import {BulletList_Stroke2_Corner0_Rounded as ListIcon} from '#/components/icons/BulletList'
 import * as ListCard from '#/components/ListCard'
diff --git a/src/view/com/notifications/NotificationFeedItem.tsx b/src/view/com/notifications/NotificationFeedItem.tsx
index ce774e888..32e7a04df 100644
--- a/src/view/com/notifications/NotificationFeedItem.tsx
+++ b/src/view/com/notifications/NotificationFeedItem.tsx
@@ -248,7 +248,7 @@ let NotificationFeedItem = ({
     : ''
 
   let a11yLabel = ''
-  let notificationContent: ReactElement
+  let notificationContent: ReactElement<any>
   let icon = (
     <HeartIconFilled
       size="xl"
diff --git a/src/view/com/pager/Pager.tsx b/src/view/com/pager/Pager.tsx
index 5eb7d7608..6d0982ad8 100644
--- a/src/view/com/pager/Pager.tsx
+++ b/src/view/com/pager/Pager.tsx
@@ -1,4 +1,5 @@
 import {
+  type JSX,
   memo,
   useCallback,
   useContext,
diff --git a/src/view/com/pager/Pager.web.tsx b/src/view/com/pager/Pager.web.tsx
index 06aac169c..ebe5432a3 100644
--- a/src/view/com/pager/Pager.web.tsx
+++ b/src/view/com/pager/Pager.web.tsx
@@ -1,5 +1,6 @@
 import {
   Children,
+  type JSX,
   useCallback,
   useImperativeHandle,
   useRef,
diff --git a/src/view/com/pager/PagerWithHeader.tsx b/src/view/com/pager/PagerWithHeader.tsx
index 57aaac074..c0c52779e 100644
--- a/src/view/com/pager/PagerWithHeader.tsx
+++ b/src/view/com/pager/PagerWithHeader.tsx
@@ -1,4 +1,4 @@
-import {memo, useCallback, useEffect, useRef, useState} from 'react'
+import {type JSX, memo, useCallback, useEffect, useRef, useState} from 'react'
 import {
   type LayoutChangeEvent,
   type NativeScrollEvent,
diff --git a/src/view/com/pager/PagerWithHeader.web.tsx b/src/view/com/pager/PagerWithHeader.web.tsx
index 3e530358a..7fed2c3c7 100644
--- a/src/view/com/pager/PagerWithHeader.web.tsx
+++ b/src/view/com/pager/PagerWithHeader.web.tsx
@@ -1,11 +1,16 @@
 import * as React from 'react'
-import {ScrollView, View} from 'react-native'
+import {type JSX} from 'react'
+import {type ScrollView, View} from 'react-native'
 import {useAnimatedRef} from 'react-native-reanimated'
 
-import {Pager, PagerRef, RenderTabBarFnProps} from '#/view/com/pager/Pager'
+import {
+  Pager,
+  type PagerRef,
+  type RenderTabBarFnProps,
+} from '#/view/com/pager/Pager'
 import {atoms as a, web} from '#/alf'
 import * as Layout from '#/components/Layout'
-import {ListMethods} from '../util/List'
+import {type ListMethods} from '../util/List'
 import {TabBar} from './TabBar'
 
 export interface PagerWithHeaderChildParams {
diff --git a/src/view/com/pager/TabBar.web.tsx b/src/view/com/pager/TabBar.web.tsx
index 8afea0019..d22650535 100644
--- a/src/view/com/pager/TabBar.web.tsx
+++ b/src/view/com/pager/TabBar.web.tsx
@@ -106,7 +106,9 @@ export function TabBar({
             <PressableWithHover
               testID={`${testID}-selector-${i}`}
               key={`${item}-${i}`}
-              ref={node => (itemRefs.current[i] = node as any)}
+              ref={node => {
+                itemRefs.current[i] = node as any
+              }}
               style={styles.item}
               hoverStyle={t.atoms.bg_contrast_25}
               onPress={() => onPressItem(i)}
diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx
index 34ebc06fa..fe6351cd4 100644
--- a/src/view/com/posts/PostFeed.tsx
+++ b/src/view/com/posts/PostFeed.tsx
@@ -1,4 +1,12 @@
-import {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'
+import {
+  type JSX,
+  memo,
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from 'react'
 import {
   ActivityIndicator,
   AppState,
diff --git a/src/view/com/util/EventStopper.tsx b/src/view/com/util/EventStopper.tsx
index 24bc47afd..fe81ac4c8 100644
--- a/src/view/com/util/EventStopper.tsx
+++ b/src/view/com/util/EventStopper.tsx
@@ -1,5 +1,4 @@
 import {View, type ViewStyle} from 'react-native'
-import type React from 'react'
 
 /**
  * This utility function captures events and stops
diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx
index 33f9b41d8..3f345c74c 100644
--- a/src/view/com/util/Link.tsx
+++ b/src/view/com/util/Link.tsx
@@ -1,4 +1,4 @@
-import {memo, useCallback, useMemo} from 'react'
+import {type JSX, memo, useCallback, useMemo} from 'react'
 import {
   type GestureResponderEvent,
   Platform,
diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx
index 7dd04bab3..b4b05a00b 100644
--- a/src/view/com/util/List.tsx
+++ b/src/view/com/util/List.tsx
@@ -57,7 +57,7 @@ let List = React.forwardRef<ListMethods, ListProps>(
       ...props
     },
     ref,
-  ): React.ReactElement => {
+  ): React.ReactElement<any> => {
     const isScrolledDown = useSharedValue(false)
     const t = useTheme()
     const dedupe = useDedupe(400)
diff --git a/src/view/com/util/List.web.tsx b/src/view/com/util/List.web.tsx
index 55dafb3dd..584738eea 100644
--- a/src/view/com/util/List.web.tsx
+++ b/src/view/com/util/List.web.tsx
@@ -1,4 +1,10 @@
-import React, {isValidElement, memo, startTransition, useRef} from 'react'
+import React, {
+  isValidElement,
+  type JSX,
+  memo,
+  startTransition,
+  useRef,
+} from 'react'
 import {
   type FlatListProps,
   StyleSheet,
@@ -202,6 +208,7 @@ function ListImpl<ItemT>(
             behavior: animated ? 'smooth' : 'instant',
           })
         },
+
         scrollToEnd({animated = true}: {animated?: boolean}) {
           const element = getScrollableNode()
           element?.scrollTo({
@@ -382,10 +389,10 @@ function EdgeVisibility({
   containerRef,
   onVisibleChange,
 }: {
-  root?: React.RefObject<HTMLDivElement> | null
+  root?: React.RefObject<HTMLDivElement | null> | null
   topMargin?: string
   bottomMargin?: string
-  containerRef: React.RefObject<Element>
+  containerRef: React.RefObject<Element | null>
   onVisibleChange: (isVisible: boolean) => void
 }) {
   const [containerHeight, setContainerHeight] = React.useState(0)
@@ -404,7 +411,7 @@ function EdgeVisibility({
 }
 
 function useResizeObserver(
-  ref: React.RefObject<Element>,
+  ref: React.RefObject<Element | null>,
   onResize: undefined | ((w: number, h: number) => void),
 ) {
   const handleResize = useNonReactiveCallback(onResize ?? (() => {}))
@@ -509,7 +516,7 @@ let Visibility = ({
   onVisibleChange,
   style,
 }: {
-  root?: React.RefObject<HTMLDivElement> | null
+  root?: React.RefObject<HTMLDivElement | null> | null
   topMargin?: string
   bottomMargin?: string
   onVisibleChange: (isVisible: boolean) => void
@@ -551,7 +558,7 @@ Visibility = React.memo(Visibility)
 
 export const List = memo(React.forwardRef(ListImpl)) as <ItemT>(
   props: ListProps<ItemT> & {ref?: React.Ref<ListMethods>},
-) => React.ReactElement
+) => React.ReactElement<any>
 
 // https://stackoverflow.com/questions/7944460/detect-safari-browser
 
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index c0feea5d4..80113f421 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -4,7 +4,6 @@ import {type AppBskyActorDefs, type ModerationDecision} from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useQueryClient} from '@tanstack/react-query'
-import type React from 'react'
 
 import {useActorStatus} from '#/lib/actor-status'
 import {makeProfileLink} from '#/lib/routes/links'
diff --git a/src/view/com/util/TimeElapsed.tsx b/src/view/com/util/TimeElapsed.tsx
index 70fed222f..146b0d523 100644
--- a/src/view/com/util/TimeElapsed.tsx
+++ b/src/view/com/util/TimeElapsed.tsx
@@ -1,5 +1,5 @@
-import React from 'react'
-import {I18n} from '@lingui/core'
+import React, {type JSX} from 'react'
+import {type I18n} from '@lingui/core'
 import {useLingui} from '@lingui/react'
 
 import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
diff --git a/src/view/com/util/ViewHeader.tsx b/src/view/com/util/ViewHeader.tsx
index 999ce52bd..4fb5c8b8f 100644
--- a/src/view/com/util/ViewHeader.tsx
+++ b/src/view/com/util/ViewHeader.tsx
@@ -1,3 +1,5 @@
+import {type JSX} from 'react'
+
 import {Header} from '#/components/Layout'
 
 /**
diff --git a/src/view/com/util/ViewSelector.tsx b/src/view/com/util/ViewSelector.tsx
index b5075707a..fb0e4b155 100644
--- a/src/view/com/util/ViewSelector.tsx
+++ b/src/view/com/util/ViewSelector.tsx
@@ -1,7 +1,7 @@
-import React, {useEffect, useState} from 'react'
+import React, {type JSX, useEffect, useState} from 'react'
 import {
-  NativeScrollEvent,
-  NativeSyntheticEvent,
+  type NativeScrollEvent,
+  type NativeSyntheticEvent,
   Pressable,
   RefreshControl,
   ScrollView,
@@ -36,7 +36,7 @@ export const ViewSelector = React.forwardRef<
     renderItem: (item: any) => JSX.Element
     ListFooterComponent?:
       | React.ComponentType<any>
-      | React.ReactElement
+      | React.ReactElement<any>
       | null
       | undefined
     onSelectView?: (viewIndex: number) => void
diff --git a/src/view/com/util/fab/FABInner.tsx b/src/view/com/util/fab/FABInner.tsx
index 77e283625..c2d64411a 100644
--- a/src/view/com/util/fab/FABInner.tsx
+++ b/src/view/com/util/fab/FABInner.tsx
@@ -1,5 +1,5 @@
-import {ComponentProps} from 'react'
-import {StyleSheet, TouchableWithoutFeedback} from 'react-native'
+import {type ComponentProps, type JSX} from 'react'
+import {StyleSheet, type TouchableWithoutFeedback} from 'react-native'
 import Animated from 'react-native-reanimated'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
 import {LinearGradient} from 'expo-linear-gradient'
diff --git a/src/view/com/util/forms/NativeDropdown.web.tsx b/src/view/com/util/forms/NativeDropdown.web.tsx
index 37df48da3..fb490b9ac 100644
--- a/src/view/com/util/forms/NativeDropdown.web.tsx
+++ b/src/view/com/util/forms/NativeDropdown.web.tsx
@@ -161,7 +161,7 @@ function DropdownContent({
   menuRef,
 }: {
   items: DropdownItem[]
-  menuRef: React.RefObject<HTMLDivElement>
+  menuRef: React.RefObject<HTMLDivElement | null>
 }) {
   const pal = usePalette('default')
   const theme = useTheme()
diff --git a/src/view/com/util/images/Gallery.tsx b/src/view/com/util/images/Gallery.tsx
index 323264ea4..b4bf905ca 100644
--- a/src/view/com/util/images/Gallery.tsx
+++ b/src/view/com/util/images/Gallery.tsx
@@ -4,7 +4,6 @@ import {Image, type ImageStyle} from 'expo-image'
 import {type AppBskyEmbedImages} from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import type React from 'react'
 
 import {type Dimensions} from '#/lib/media/types'
 import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
diff --git a/src/view/screens/Storybook/Dialogs.tsx b/src/view/screens/Storybook/Dialogs.tsx
index 4b4d4191f..3c1d4f20b 100644
--- a/src/view/screens/Storybook/Dialogs.tsx
+++ b/src/view/screens/Storybook/Dialogs.tsx
@@ -22,7 +22,7 @@ export function Dialogs() {
     React.useState<boolean>()
   const [shouldRenderUnmountTest, setShouldRenderUnmountTest] =
     React.useState(false)
-  const unmountTestInterval = React.useRef<number>()
+  const unmountTestInterval = React.useRef<number>(undefined)
 
   const onUnmountTestStartPressWithClose = () => {
     setShouldRenderUnmountTest(true)
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
index 4a797e578..832e4fc3a 100644
--- a/src/view/shell/Drawer.tsx
+++ b/src/view/shell/Drawer.tsx
@@ -1,4 +1,4 @@
-import React, {type ComponentProps} from 'react'
+import React, {type ComponentProps, type JSX} from 'react'
 import {Linking, ScrollView, TouchableOpacity, View} from 'react-native'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
 import {msg, Plural, plural, Trans} from '@lingui/macro'
diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx
index 01aa4afc4..4b596869e 100644
--- a/src/view/shell/bottom-bar/BottomBar.tsx
+++ b/src/view/shell/bottom-bar/BottomBar.tsx
@@ -1,4 +1,4 @@
-import {useCallback} from 'react'
+import {type JSX, useCallback} from 'react'
 import {type GestureResponderEvent, View} from 'react-native'
 import Animated from 'react-native-reanimated'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
diff --git a/src/view/shell/bottom-bar/BottomBarWeb.tsx b/src/view/shell/bottom-bar/BottomBarWeb.tsx
index 8dce85cd1..c884673cd 100644
--- a/src/view/shell/bottom-bar/BottomBarWeb.tsx
+++ b/src/view/shell/bottom-bar/BottomBarWeb.tsx
@@ -230,7 +230,7 @@ export function BottomBarWeb() {
 }
 
 const NavItem: React.FC<{
-  children: (props: {isActive: boolean}) => React.ReactChild
+  children: (props: {isActive: boolean}) => React.ReactNode
   href: string
   routeName: string
   hasNew?: boolean
diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx
index 5c22198c8..b89d77294 100644
--- a/src/view/shell/desktop/LeftNav.tsx
+++ b/src/view/shell/desktop/LeftNav.tsx
@@ -1,4 +1,4 @@
-import {useCallback, useMemo, useState} from 'react'
+import {type JSX, useCallback, useMemo, useState} from 'react'
 import {StyleSheet, View} from 'react-native'
 import {type AppBskyActorDefs} from '@atproto/api'
 import {msg, plural, Trans} from '@lingui/macro'