diff --git a/src/components/Modal/ReanimatedModal/Backdrop/index.web.tsx b/src/components/Modal/ReanimatedModal/Backdrop/index.web.tsx
index 253da05ed50b..10f4f54c9263 100644
--- a/src/components/Modal/ReanimatedModal/Backdrop/index.web.tsx
+++ b/src/components/Modal/ReanimatedModal/Backdrop/index.web.tsx
@@ -1,15 +1,14 @@
 import React, {useMemo} from 'react';
 import {View} from 'react-native';
-import Animated, {Easing, Keyframe} from 'react-native-reanimated';
+import Animated, {Keyframe} from 'react-native-reanimated';
 import type {BackdropProps} from '@components/Modal/ReanimatedModal/types';
+import {getModalInAnimation, getModalOutAnimation} from '@components/Modal/ReanimatedModal/utils';
 import {PressableWithoutFeedback} from '@components/Pressable';
 import useLocalize from '@hooks/useLocalize';
 import useThemeStyles from '@hooks/useThemeStyles';
 import variables from '@styles/variables';
 import CONST from '@src/CONST';
 
-const easing = Easing.bezier(0.76, 0.0, 0.24, 1.0).factory();
-
 function Backdrop({
     style,
     customBackdrop,
@@ -23,25 +22,13 @@ function Backdrop({
     const {translate} = useLocalize();
 
     const Entering = useMemo(() => {
-        const FadeIn = new Keyframe({
-            from: {opacity: 0},
-            to: {
-                opacity: 0.72,
-                easing,
-            },
-        });
+        const FadeIn = new Keyframe(getModalInAnimation('fadeIn'));
 
         return FadeIn.duration(animationInTiming);
     }, [animationInTiming]);
 
     const Exiting = useMemo(() => {
-        const FadeOut = new Keyframe({
-            from: {opacity: 0.72},
-            to: {
-                opacity: 0,
-                easing,
-            },
-        });
+        const FadeOut = new Keyframe(getModalOutAnimation('fadeOut'));
 
         return FadeOut.duration(animationOutTiming);
     }, [animationOutTiming]);
diff --git a/src/components/Modal/ReanimatedModal/Container/GestureHandler.tsx b/src/components/Modal/ReanimatedModal/Container/GestureHandler.tsx
new file mode 100644
index 000000000000..a03aa2582e71
--- /dev/null
+++ b/src/components/Modal/ReanimatedModal/Container/GestureHandler.tsx
@@ -0,0 +1,78 @@
+import type {PropsWithChildren} from 'react';
+import React, {useMemo} from 'react';
+import type {GestureStateChangeEvent, GestureType, PanGestureHandlerEventPayload} from 'react-native-gesture-handler';
+import {Gesture, GestureDetector} from 'react-native-gesture-handler';
+import {runOnJS, useSharedValue} from 'react-native-reanimated';
+import type {GestureHandlerProps, SwipeDirection} from '@components/Modal/ReanimatedModal/types';
+import CONST from '@src/CONST';
+
+function hasSwipeEnded(
+    e: GestureStateChangeEvent<PanGestureHandlerEventPayload>,
+    initialPosition: {x: number; y: number},
+    swipeThreshold: number,
+    swipeDirection?: SwipeDirection | SwipeDirection[],
+    onSwipeComplete?: () => void,
+) {
+    'worklet';
+
+    // eslint-disable-next-line @typescript-eslint/prefer-optional-chain
+    if (!swipeDirection || !swipeDirection?.length || !onSwipeComplete) {
+        return;
+    }
+    const directions = Array.isArray(swipeDirection) ? swipeDirection : [swipeDirection];
+
+    for (const direction of directions) {
+        switch (direction) {
+            case CONST.SWIPE_DIRECTION.RIGHT:
+                if (e.translationX - initialPosition.x > swipeThreshold) {
+                    runOnJS(onSwipeComplete)();
+                }
+                break;
+            case CONST.SWIPE_DIRECTION.LEFT:
+                if (initialPosition.x - e.translationX > swipeThreshold) {
+                    runOnJS(onSwipeComplete)();
+                }
+                break;
+            case CONST.SWIPE_DIRECTION.UP:
+                if (initialPosition.y - e.translationY > swipeThreshold) {
+                    runOnJS(onSwipeComplete)();
+                }
+                break;
+            case CONST.SWIPE_DIRECTION.DOWN:
+                if (e.translationY - initialPosition.y > swipeThreshold) {
+                    runOnJS(onSwipeComplete)();
+                }
+                break;
+            default:
+                throw new Error('Unknown swipe direction');
+        }
+    }
+}
+
+function GestureHandler({swipeDirection, onSwipeComplete, swipeThreshold = 100, children}: PropsWithChildren<GestureHandlerProps>) {
+    const initialTranslationX = useSharedValue(0);
+    const initialTranslationY = useSharedValue(0);
+    const panGesture: GestureType = useMemo(
+        () =>
+            Gesture.Pan()
+                .onStart((e) => {
+                    initialTranslationX.set(e.translationX);
+                    initialTranslationY.set(e.translationY);
+                })
+                .onEnd((e) => {
+                    hasSwipeEnded(e, {x: initialTranslationX.get(), y: initialTranslationY.get()}, swipeThreshold, swipeDirection, onSwipeComplete);
+                }),
+        [initialTranslationX, initialTranslationY, onSwipeComplete, swipeDirection, swipeThreshold],
+    );
+
+    // eslint-disable-next-line @typescript-eslint/prefer-optional-chain
+    if (!swipeDirection || !swipeDirection?.length || !onSwipeComplete) {
+        return children;
+    }
+
+    return <GestureDetector gesture={panGesture}>{children}</GestureDetector>;
+}
+
+GestureHandler.displayName = 'GestureHandler';
+
+export default GestureHandler;
diff --git a/src/components/Modal/ReanimatedModal/Container/index.tsx b/src/components/Modal/ReanimatedModal/Container/index.tsx
index 1ab545e50c6b..d9c50b8d9b6d 100644
--- a/src/components/Modal/ReanimatedModal/Container/index.tsx
+++ b/src/components/Modal/ReanimatedModal/Container/index.tsx
@@ -6,6 +6,7 @@ import type {ContainerProps} from '@components/Modal/ReanimatedModal/types';
 import {getModalInAnimation, getModalOutAnimation} from '@components/Modal/ReanimatedModal/utils';
 import useThemeStyles from '@hooks/useThemeStyles';
 import CONST from '@src/CONST';
+import GestureHandler from './GestureHandler';
 
 function Container({
     style,
@@ -16,6 +17,9 @@ function Container({
     animationIn,
     animationOut,
     type,
+    onSwipeComplete,
+    swipeDirection,
+    swipeThreshold = 100,
     ...props
 }: Partial<ReanimatedModalProps> & ContainerProps) {
     const styles = useThemeStyles();
@@ -46,13 +50,19 @@ function Container({
             // eslint-disable-next-line react/jsx-props-no-spreading
             {...props}
         >
-            <Animated.View
-                style={[styles.modalAnimatedContainer, type !== CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED && styles.flex1]}
-                entering={Entering}
-                exiting={Exiting}
+            <GestureHandler
+                swipeThreshold={swipeThreshold}
+                swipeDirection={swipeDirection}
+                onSwipeComplete={onSwipeComplete}
             >
-                {props.children}
-            </Animated.View>
+                <Animated.View
+                    style={[styles.modalAnimatedContainer, type !== CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED && styles.flex1]}
+                    entering={Entering}
+                    exiting={Exiting}
+                >
+                    {props.children}
+                </Animated.View>
+            </GestureHandler>
         </View>
     );
 }
diff --git a/src/components/Modal/ReanimatedModal/index.tsx b/src/components/Modal/ReanimatedModal/index.tsx
index ab873254fc29..db0c3b8b22c6 100644
--- a/src/components/Modal/ReanimatedModal/index.tsx
+++ b/src/components/Modal/ReanimatedModal/index.tsx
@@ -37,6 +37,9 @@ function ReanimatedModal({
     style,
     type,
     statusBarTranslucent = false,
+    onSwipeComplete,
+    swipeDirection,
+    swipeThreshold,
     ...props
 }: ReanimatedModalProps) {
     const [isVisibleState, setIsVisibleState] = useState(isVisible);
@@ -148,6 +151,8 @@ function ReanimatedModal({
             animationOut={animationOut as AnimationOutType}
             style={style}
             type={type}
+            onSwipeComplete={onSwipeComplete}
+            swipeDirection={swipeDirection}
         >
             {children}
         </Container>
diff --git a/src/components/Modal/ReanimatedModal/types.ts b/src/components/Modal/ReanimatedModal/types.ts
index cf03deb66340..dbcc19ae82df 100644
--- a/src/components/Modal/ReanimatedModal/types.ts
+++ b/src/components/Modal/ReanimatedModal/types.ts
@@ -13,13 +13,27 @@ type GestureProps = {
     deviceWidth?: number | null;
 };
 
+type SwipeDirection = ValueOf<typeof CONST.SWIPE_DIRECTION>;
+
+type GestureHandlerProps = {
+    /** Callback to be fired on swipe gesture. */
+    onSwipeComplete?: () => void;
+
+    /** Threshold for swipe gesture. */
+    swipeThreshold: number;
+
+    /** Threshold for swipe gesture. */
+    swipeDirection?: SwipeDirection | SwipeDirection[];
+};
+
 type AnimationInType = 'fadeIn' | 'slideInUp' | 'slideInRight';
 type AnimationOutType = 'fadeOut' | 'slideOutDown' | 'slideOutRight';
 
 type AnimationOut = ValueOf<Pick<ReactNativeModalProps, 'animationOut'>>;
 
 type ReanimatedModalProps = ViewProps &
-    GestureProps & {
+    GestureProps &
+    GestureHandlerProps & {
         /** Content inside the modal */
         children: ReactNode;
 
@@ -168,4 +182,4 @@ type ContainerProps = {
 };
 
 export default ReanimatedModalProps;
-export type {BackdropProps, ContainerProps, AnimationOut, AnimationInType, AnimationOutType};
+export type {BackdropProps, ContainerProps, GestureHandlerProps, AnimationOut, AnimationInType, AnimationOutType, SwipeDirection};
diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx
index ae855804beb9..131d54416d95 100644
--- a/src/components/Search/SearchRouter/SearchRouter.tsx
+++ b/src/components/Search/SearchRouter/SearchRouter.tsx
@@ -3,6 +3,7 @@ import {deepEqual} from 'fast-equals';
 import React, {forwardRef, useCallback, useEffect, useRef, useState} from 'react';
 import type {TextInputProps} from 'react-native';
 import {InteractionManager, Keyboard, View} from 'react-native';
+import {Gesture, GestureDetector, GestureHandlerRootView} from 'react-native-gesture-handler';
 import type {ValueOf} from 'type-fest';
 import HeaderWithBackButton from '@components/HeaderWithBackButton';
 import * as Expensicons from '@components/Icon/Expensicons';
@@ -428,62 +429,64 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla
     const isRecentSearchesDataLoaded = !isLoadingOnyxValue(recentSearchesMetadata);
 
     return (
-        <View
-            style={[styles.flex1, modalWidth, styles.h100, !shouldUseNarrowLayout && styles.mh85vh]}
-            testID={SearchRouter.displayName}
-            ref={ref}
-            onStartShouldSetResponder={() => true}
-            onResponderRelease={Keyboard.dismiss}
-        >
-            {shouldUseNarrowLayout && (
-                <HeaderWithBackButton
-                    title={translate('common.search')}
-                    onBackButtonPress={() => onRouterClose()}
-                    shouldDisplayHelpButton={false}
-                />
-            )}
-            {isRecentSearchesDataLoaded && (
-                <>
-                    <SearchInputSelectionWrapper
-                        value={textInputValue}
-                        isFullWidth={shouldUseNarrowLayout}
-                        onSearchQueryChange={onSearchQueryChange}
-                        onSubmit={() => {
-                            const focusedOption = listRef.current?.getFocusedOption();
-
-                            if (!focusedOption) {
-                                submitSearch(textInputValue);
-                                return;
-                            }
-
-                            onListItemPress(focusedOption);
-                        }}
-                        caretHidden={shouldHideInputCaret}
-                        autocompleteListRef={listRef}
-                        shouldShowOfflineMessage
-                        wrapperStyle={{...styles.border, ...styles.alignItemsCenter}}
-                        outerWrapperStyle={[shouldUseNarrowLayout ? styles.mv3 : styles.mv2, shouldUseNarrowLayout ? styles.mh5 : styles.mh2]}
-                        wrapperFocusedStyle={styles.borderColorFocus}
-                        isSearchingForReports={isSearchingForReports}
-                        selection={selection}
-                        substitutionMap={autocompleteSubstitutions}
-                        ref={textInputRef}
-                    />
-                    <SearchAutocompleteList
-                        autocompleteQueryValue={autocompleteQueryValue || textInputValue}
-                        handleSearch={searchInServer}
-                        searchQueryItem={searchQueryItem}
-                        getAdditionalSections={getAdditionalSections}
-                        onListItemPress={onListItemPress}
-                        setTextQuery={setTextAndUpdateSelection}
-                        updateAutocompleteSubstitutions={updateAutocompleteSubstitutions}
-                        onHighlightFirstItem={() => listRef.current?.updateAndScrollToFocusedIndex(1)}
-                        ref={listRef}
-                        textInputRef={textInputRef}
-                    />
-                </>
-            )}
-        </View>
+        <GestureHandlerRootView style={{flex: 1}}>
+            <GestureDetector gesture={Gesture.Tap().runOnJS(true).onFinalize(Keyboard.dismiss)}>
+                <View
+                    style={[styles.flex1, modalWidth, styles.h100, !shouldUseNarrowLayout && styles.mh85vh]}
+                    testID={SearchRouter.displayName}
+                    ref={ref}
+                >
+                    {shouldUseNarrowLayout && (
+                        <HeaderWithBackButton
+                            title={translate('common.search')}
+                            onBackButtonPress={() => onRouterClose()}
+                            shouldDisplayHelpButton={false}
+                        />
+                    )}
+                    {isRecentSearchesDataLoaded && (
+                        <>
+                            <SearchInputSelectionWrapper
+                                value={textInputValue}
+                                isFullWidth={shouldUseNarrowLayout}
+                                onSearchQueryChange={onSearchQueryChange}
+                                onSubmit={() => {
+                                    const focusedOption = listRef.current?.getFocusedOption();
+
+                                    if (!focusedOption) {
+                                        submitSearch(textInputValue);
+                                        return;
+                                    }
+
+                                    onListItemPress(focusedOption);
+                                }}
+                                caretHidden={shouldHideInputCaret}
+                                autocompleteListRef={listRef}
+                                shouldShowOfflineMessage
+                                wrapperStyle={{...styles.border, ...styles.alignItemsCenter}}
+                                outerWrapperStyle={[shouldUseNarrowLayout ? styles.mv3 : styles.mv2, shouldUseNarrowLayout ? styles.mh5 : styles.mh2]}
+                                wrapperFocusedStyle={styles.borderColorFocus}
+                                isSearchingForReports={isSearchingForReports}
+                                selection={selection}
+                                substitutionMap={autocompleteSubstitutions}
+                                ref={textInputRef}
+                            />
+                            <SearchAutocompleteList
+                                autocompleteQueryValue={autocompleteQueryValue || textInputValue}
+                                handleSearch={searchInServer}
+                                searchQueryItem={searchQueryItem}
+                                getAdditionalSections={getAdditionalSections}
+                                onListItemPress={onListItemPress}
+                                setTextQuery={setTextAndUpdateSelection}
+                                updateAutocompleteSubstitutions={updateAutocompleteSubstitutions}
+                                onHighlightFirstItem={() => listRef.current?.updateAndScrollToFocusedIndex(1)}
+                                ref={listRef}
+                                textInputRef={textInputRef}
+                            />
+                        </>
+                    )}
+                </View>
+            </GestureDetector>
+        </GestureHandlerRootView>
     );
 }
 
diff --git a/src/components/Search/SearchRouter/SearchRouterModal.tsx b/src/components/Search/SearchRouter/SearchRouterModal.tsx
index c4a6b5d128bf..ef93bb0188f7 100644
--- a/src/components/Search/SearchRouter/SearchRouterModal.tsx
+++ b/src/components/Search/SearchRouter/SearchRouterModal.tsx
@@ -20,7 +20,7 @@ function SearchRouterModal() {
     const [shouldHideInputCaret, setShouldHideInputCaret] = useState(isMobileWebIOS);
 
     const modalType = shouldUseNarrowLayout ? CONST.MODAL.MODAL_TYPE.CENTERED_SWIPEABLE_TO_RIGHT : CONST.MODAL.MODAL_TYPE.POPOVER;
-
+    // For now were only enabling shouldUseReanimatedModal narrow layouts. On wide ones it's a popover and it is not migrated yet.
     return (
         <Modal
             type={modalType}
@@ -35,6 +35,7 @@ function SearchRouterModal() {
             onModalShow={() => setShouldHideInputCaret(false)}
             shouldApplySidePanelOffset={!shouldUseNarrowLayout}
             enableEdgeToEdgeBottomSafeAreaPadding
+            shouldUseReanimatedModal={shouldUseNarrowLayout}
         >
             <ScreenWrapperContainer
                 testID={SearchRouterModal.displayName}
