From e6c0fae218d697091ced61f36eef82372247d605 Mon Sep 17 00:00:00 2001
From: Hubert Sosinski <hubert.sosinski@callstack.com>
Date: Fri, 28 Nov 2025 19:48:12 +0100
Subject: [PATCH 1/9] Introduce hook useSkeletonSpan to track Skeletons'
 duration

---
 src/CONST/index.ts                    |  1 +
 src/components/AvatarSkeleton.tsx     |  3 ++-
 src/libs/telemetry/useSkeletonSpan.ts | 22 ++++++++++++++++++++++
 3 files changed, 25 insertions(+), 1 deletion(-)
 create mode 100644 src/libs/telemetry/useSkeletonSpan.ts

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 44414c94fbab..8c947fba3a71 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -1682,6 +1682,7 @@ const CONST = {
         SPAN_OPEN_SEARCH_ROUTER: 'ManualOpenSearchRouter',
         SPAN_OPEN_CREATE_EXPENSE: 'ManualOpenCreateExpense',
         SPAN_SEND_MESSAGE: 'ManualSendMessage',
+        SPAN_SKELETON: 'ManualSkeleton',
         // Attribute names
         ATTRIBUTE_IOU_TYPE: 'iou_type',
         ATTRIBUTE_IOU_REQUEST_TYPE: 'iou_request_type',
diff --git a/src/components/AvatarSkeleton.tsx b/src/components/AvatarSkeleton.tsx
index 6e0a4f407d70..c11a57b6c677 100644
--- a/src/components/AvatarSkeleton.tsx
+++ b/src/components/AvatarSkeleton.tsx
@@ -3,12 +3,13 @@ import {Circle} from 'react-native-svg';
 import type {ValueOf} from 'type-fest';
 import useStyleUtils from '@hooks/useStyleUtils';
 import useTheme from '@hooks/useTheme';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import CONST from '@src/CONST';
 import SkeletonViewContentLoader from './SkeletonViewContentLoader';
 
 function AvatarSkeleton({size = CONST.AVATAR_SIZE.SMALL}: {size?: ValueOf<typeof CONST.AVATAR_SIZE>}) {
     const theme = useTheme();
-
+    useSkeletonSpan('AvatarSkeleton');
     const StyleUtils = useStyleUtils();
     const avatarSize = StyleUtils.getAvatarSize(size);
     const skeletonCircleRadius = avatarSize / 2;
diff --git a/src/libs/telemetry/useSkeletonSpan.ts b/src/libs/telemetry/useSkeletonSpan.ts
new file mode 100644
index 000000000000..d6a4287c38d8
--- /dev/null
+++ b/src/libs/telemetry/useSkeletonSpan.ts
@@ -0,0 +1,22 @@
+/**
+ * This file contains logic related to tracking skeleton across the application.
+ */
+import CONST from '@src/CONST';
+import {endSpan, startSpan} from '@libs/telemetry/activeSpans';
+import {useEffect, useId, useMemo} from 'react';
+
+function useSkeletonSpan(component: string) {
+    const reactId = useId();
+
+    useEffect(() => {
+        const spanId = `${CONST.TELEMETRY.SPAN_SKELETON}_${component}_${reactId}`
+        startSpan(spanId, {
+            op: CONST.TELEMETRY.SPAN_SKELETON,
+            name: component,
+        });
+
+        return () => endSpan(spanId);
+    }, []);
+}
+
+export {useSkeletonSpan};

From 34952c0c18a5876999754a5780db6942830af483 Mon Sep 17 00:00:00 2001
From: Hubert Sosinski <hubert.sosinski@callstack.com>
Date: Sat, 29 Nov 2025 13:59:48 +0100
Subject: [PATCH 2/9] Add useSkeletonSpan to all components

---
 src/components/AccountSwitcherSkeletonView/index.tsx        | 2 ++
 src/components/MoneyReportHeaderStatusBarSkeleton.tsx       | 2 ++
 .../ReportActionsListLoadingSkeleton.tsx                    | 3 +++
 src/components/OptionsListSkeletonView.tsx                  | 2 ++
 src/components/ReportHeaderSkeletonView.tsx                 | 2 ++
 src/components/Skeletons/CardRowSkeleton.tsx                | 2 ++
 src/components/Skeletons/MergeExpensesSkeleton.tsx          | 2 ++
 src/components/Skeletons/SearchFiltersSkeleton.tsx          | 2 ++
 src/components/Skeletons/SearchInputSelectionSkeleton.tsx   | 2 ++
 src/components/Skeletons/SearchRowSkeleton.tsx              | 2 ++
 src/components/Skeletons/TabNavigatorSkeleton.tsx           | 2 ++
 src/components/Skeletons/TableRowSkeleton.tsx               | 2 ++
 src/components/Skeletons/UnreportedExpensesSkeleton.tsx     | 2 ++
 src/components/Skeletons/WorkspaceRowSkeleton.tsx           | 2 ++
 src/components/TransactionPreviewSkeletonView.tsx           | 2 ++
 src/libs/telemetry/useSkeletonSpan.ts                       | 6 +++---
 16 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/src/components/AccountSwitcherSkeletonView/index.tsx b/src/components/AccountSwitcherSkeletonView/index.tsx
index 379a4094e032..a99315120b45 100644
--- a/src/components/AccountSwitcherSkeletonView/index.tsx
+++ b/src/components/AccountSwitcherSkeletonView/index.tsx
@@ -6,6 +6,7 @@ import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
 import useStyleUtils from '@hooks/useStyleUtils';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import CONST from '@src/CONST';
 
 type AccountSwitcherSkeletonViewProps = {
@@ -20,6 +21,7 @@ function AccountSwitcherSkeletonView({shouldAnimate = true, avatarSize = CONST.A
     const theme = useTheme();
     const styles = useThemeStyles();
     const StyleUtils = useStyleUtils();
+    useSkeletonSpan('AccountSwitcherSkeletonView');
     const avatarPlaceholderSize = StyleUtils.getAvatarSize(avatarSize);
     const avatarPlaceholderRadius = avatarPlaceholderSize / 2;
     const startPositionX = avatarPlaceholderRadius;
diff --git a/src/components/MoneyReportHeaderStatusBarSkeleton.tsx b/src/components/MoneyReportHeaderStatusBarSkeleton.tsx
index 6294fa4c80ba..c6f88dae19a5 100644
--- a/src/components/MoneyReportHeaderStatusBarSkeleton.tsx
+++ b/src/components/MoneyReportHeaderStatusBarSkeleton.tsx
@@ -3,11 +3,13 @@ import {View} from 'react-native';
 import {Rect} from 'react-native-svg';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import SkeletonViewContentLoader from './SkeletonViewContentLoader';
 
 function MoneyReportHeaderStatusBarSkeleton() {
     const styles = useThemeStyles();
     const theme = useTheme();
+    useSkeletonSpan('MoneyReportHeaderStatusBarSkeleton');
 
     return (
         <View style={[styles.dFlex, styles.flexRow, styles.overflowHidden, styles.w100, {height: 28}]}>
diff --git a/src/components/MoneyRequestReportView/ReportActionsListLoadingSkeleton.tsx b/src/components/MoneyRequestReportView/ReportActionsListLoadingSkeleton.tsx
index b5bece629b5d..f7e6dd955f67 100644
--- a/src/components/MoneyRequestReportView/ReportActionsListLoadingSkeleton.tsx
+++ b/src/components/MoneyRequestReportView/ReportActionsListLoadingSkeleton.tsx
@@ -1,8 +1,11 @@
 import React from 'react';
 import Animated, {FadeIn, FadeOut} from 'react-native-reanimated';
 import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 
 function ReportActionsListLoadingSkeleton() {
+    useSkeletonSpan('ReportActionsListLoadingSkeleton');
+
     return (
         <Animated.View
             entering={FadeIn}
diff --git a/src/components/OptionsListSkeletonView.tsx b/src/components/OptionsListSkeletonView.tsx
index 22e7633fa553..0b214f7dc2b2 100644
--- a/src/components/OptionsListSkeletonView.tsx
+++ b/src/components/OptionsListSkeletonView.tsx
@@ -1,6 +1,7 @@
 import React from 'react';
 import {Circle, Rect} from 'react-native-svg';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import ItemListSkeletonView from './Skeletons/ItemListSkeletonView';
 
 function getLinedWidth(index: number): string {
@@ -26,6 +27,7 @@ type OptionsListSkeletonViewProps = {
 
 function OptionsListSkeletonView({shouldAnimate = true, shouldStyleAsTable = false, gradientOpacityEnabled = false, fixedNumItems, speed}: OptionsListSkeletonViewProps) {
     const styles = useThemeStyles();
+    useSkeletonSpan('OptionsListSkeletonView');
 
     return (
         <ItemListSkeletonView
diff --git a/src/components/ReportHeaderSkeletonView.tsx b/src/components/ReportHeaderSkeletonView.tsx
index c1db3d0ef0d5..4df82185e890 100644
--- a/src/components/ReportHeaderSkeletonView.tsx
+++ b/src/components/ReportHeaderSkeletonView.tsx
@@ -5,6 +5,7 @@ import useLocalize from '@hooks/useLocalize';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import CONST from '@src/CONST';
 import Icon from './Icon';
 import * as Expensicons from './Icon/Expensicons';
@@ -21,6 +22,7 @@ function ReportHeaderSkeletonView({shouldAnimate = true, onBackButtonPress = ()
     const styles = useThemeStyles();
     const {translate} = useLocalize();
     const {shouldUseNarrowLayout} = useResponsiveLayout();
+    useSkeletonSpan('ReportHeaderSkeletonView');
     const height = styles.headerBarHeight.height;
     const radius = 20;
     const circleY = height / 2;
diff --git a/src/components/Skeletons/CardRowSkeleton.tsx b/src/components/Skeletons/CardRowSkeleton.tsx
index 4183226d360d..ead6e54eaad5 100644
--- a/src/components/Skeletons/CardRowSkeleton.tsx
+++ b/src/components/Skeletons/CardRowSkeleton.tsx
@@ -3,6 +3,7 @@ import {Circle, Rect} from 'react-native-svg';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useWindowDimensions from '@hooks/useWindowDimensions';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import variables from '@styles/variables';
 import ItemListSkeletonView from './ItemListSkeletonView';
 
@@ -25,6 +26,7 @@ function CardRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEn
     const styles = useThemeStyles();
     const {windowWidth} = useWindowDimensions();
     const {shouldUseNarrowLayout} = useResponsiveLayout();
+    useSkeletonSpan('CardRowSkeleton');
 
     return (
         <ItemListSkeletonView
diff --git a/src/components/Skeletons/MergeExpensesSkeleton.tsx b/src/components/Skeletons/MergeExpensesSkeleton.tsx
index 1fb1a647ea80..5b3480dd413e 100644
--- a/src/components/Skeletons/MergeExpensesSkeleton.tsx
+++ b/src/components/Skeletons/MergeExpensesSkeleton.tsx
@@ -2,6 +2,7 @@ import React, {useCallback, useLayoutEffect, useRef} from 'react';
 import {View} from 'react-native';
 import {Rect} from 'react-native-svg';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import ItemListSkeletonView from './ItemListSkeletonView';
 
 const barHeight = 7;
@@ -18,6 +19,7 @@ function MergeExpensesSkeleton({fixedNumItems, speed}: MergeExpensesSkeletonProp
     const containerRef = useRef<View>(null);
     const styles = useThemeStyles();
     const [pageWidth, setPageWidth] = React.useState(0);
+    useSkeletonSpan('MergeExpensesSkeleton');
     useLayoutEffect(() => {
         containerRef.current?.measure((x, y, width) => {
             setPageWidth(width - 24);
diff --git a/src/components/Skeletons/SearchFiltersSkeleton.tsx b/src/components/Skeletons/SearchFiltersSkeleton.tsx
index bdeeb8ab4d6e..9cd6bc0c791a 100644
--- a/src/components/Skeletons/SearchFiltersSkeleton.tsx
+++ b/src/components/Skeletons/SearchFiltersSkeleton.tsx
@@ -4,6 +4,7 @@ import {Rect} from 'react-native-svg';
 import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 
 type SearchFiltersSkeletonProps = {
     shouldAnimate?: boolean;
@@ -12,6 +13,7 @@ type SearchFiltersSkeletonProps = {
 function SearchFiltersSkeleton({shouldAnimate = true}: SearchFiltersSkeletonProps) {
     const theme = useTheme();
     const styles = useThemeStyles();
+    useSkeletonSpan('SearchFiltersSkeleton');
 
     const skeletonCount = new Array(5).fill(0);
 
diff --git a/src/components/Skeletons/SearchInputSelectionSkeleton.tsx b/src/components/Skeletons/SearchInputSelectionSkeleton.tsx
index 2207f41e4d5d..084aafab162d 100644
--- a/src/components/Skeletons/SearchInputSelectionSkeleton.tsx
+++ b/src/components/Skeletons/SearchInputSelectionSkeleton.tsx
@@ -4,11 +4,13 @@ import {Rect} from 'react-native-svg';
 import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import variables from '@styles/variables';
 
 function SearchInputSelectionSkeleton() {
     const theme = useTheme();
     const styles = useThemeStyles();
+    useSkeletonSpan('SearchInputSelectionSkeleton');
 
     return (
         <View style={[styles.searchRouterTextInputContainer, styles.searchAutocompleteInputResults, styles.br2, styles.justifyContentCenter]}>
diff --git a/src/components/Skeletons/SearchRowSkeleton.tsx b/src/components/Skeletons/SearchRowSkeleton.tsx
index 709918095d8a..231e11de780f 100644
--- a/src/components/Skeletons/SearchRowSkeleton.tsx
+++ b/src/components/Skeletons/SearchRowSkeleton.tsx
@@ -5,6 +5,7 @@ import {Circle, Rect} from 'react-native-svg';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useWindowDimensions from '@hooks/useWindowDimensions';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import variables from '@styles/variables';
 import CONST from '@src/CONST';
 import ItemListSkeletonView from './ItemListSkeletonView';
@@ -36,6 +37,7 @@ function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacity
     const styles = useThemeStyles();
     const {windowWidth} = useWindowDimensions();
     const {shouldUseNarrowLayout, isLargeScreenWidth} = useResponsiveLayout();
+    useSkeletonSpan('SearchRowSkeleton');
 
     if (shouldUseNarrowLayout) {
         return (
diff --git a/src/components/Skeletons/TabNavigatorSkeleton.tsx b/src/components/Skeletons/TabNavigatorSkeleton.tsx
index 3b74b808eacd..b0f6affecc6a 100644
--- a/src/components/Skeletons/TabNavigatorSkeleton.tsx
+++ b/src/components/Skeletons/TabNavigatorSkeleton.tsx
@@ -4,10 +4,12 @@ import {Rect} from 'react-native-svg';
 import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 
 function TabNavigatorSkeleton() {
     const styles = useThemeStyles();
     const theme = useTheme();
+    useSkeletonSpan('TabNavigatorSkeleton');
 
     return (
         <View style={[styles.flexRow, styles.w100, styles.justifyContentBetween, styles.h10]}>
diff --git a/src/components/Skeletons/TableRowSkeleton.tsx b/src/components/Skeletons/TableRowSkeleton.tsx
index eb1237975dd4..38e39878d9c3 100644
--- a/src/components/Skeletons/TableRowSkeleton.tsx
+++ b/src/components/Skeletons/TableRowSkeleton.tsx
@@ -1,6 +1,7 @@
 import React from 'react';
 import {Circle, Rect} from 'react-native-svg';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import ItemListSkeletonView from './ItemListSkeletonView';
 
 type TableListItemSkeletonProps = {
@@ -15,6 +16,7 @@ const longBarWidth = '124';
 
 function TableListItemSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEnabled = false}: TableListItemSkeletonProps) {
     const styles = useThemeStyles();
+    useSkeletonSpan('TableRowSkeleton');
 
     return (
         <ItemListSkeletonView
diff --git a/src/components/Skeletons/UnreportedExpensesSkeleton.tsx b/src/components/Skeletons/UnreportedExpensesSkeleton.tsx
index 3b209739d35d..787e19353d08 100644
--- a/src/components/Skeletons/UnreportedExpensesSkeleton.tsx
+++ b/src/components/Skeletons/UnreportedExpensesSkeleton.tsx
@@ -2,6 +2,7 @@ import React, {useCallback, useLayoutEffect, useRef} from 'react';
 import {View} from 'react-native';
 import {Rect} from 'react-native-svg';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import ItemListSkeletonView from './ItemListSkeletonView';
 
 function getMessageSkeletonWidth(index: number) {
@@ -34,6 +35,7 @@ function UnreportedExpensesSkeleton({fixedNumberOfItems}: {fixedNumberOfItems?:
     const containerRef = useRef<View>(null);
     const styles = useThemeStyles();
     const [pageWidth, setPageWidth] = React.useState(0);
+    useSkeletonSpan('UnreportedExpensesSkeleton');
     useLayoutEffect(() => {
         containerRef.current?.measure((x, y, width) => {
             setPageWidth(width - 40);
diff --git a/src/components/Skeletons/WorkspaceRowSkeleton.tsx b/src/components/Skeletons/WorkspaceRowSkeleton.tsx
index 5c9f18ef4ef3..6c00cc3bbfb0 100644
--- a/src/components/Skeletons/WorkspaceRowSkeleton.tsx
+++ b/src/components/Skeletons/WorkspaceRowSkeleton.tsx
@@ -3,6 +3,7 @@ import {Rect} from 'react-native-svg';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useWindowDimensions from '@hooks/useWindowDimensions';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import variables from '@styles/variables';
 import ItemListSkeletonView from './ItemListSkeletonView';
 
@@ -22,6 +23,7 @@ function WorkspaceRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpac
     const styles = useThemeStyles();
     const {windowWidth} = useWindowDimensions();
     const {shouldUseNarrowLayout} = useResponsiveLayout();
+    useSkeletonSpan('WorkspaceRowSkeleton');
     // We calculate the width of the sections on the skeleton by first calculating the skeleton view width
     // Then we subtract the width by 66, which is the x position of the first part.
     const partWidth = Math.floor((windowWidth - leftPaneWidth - gapWidth * 2 - 66) / 3);
diff --git a/src/components/TransactionPreviewSkeletonView.tsx b/src/components/TransactionPreviewSkeletonView.tsx
index 55ec76d0e086..cb3330581698 100644
--- a/src/components/TransactionPreviewSkeletonView.tsx
+++ b/src/components/TransactionPreviewSkeletonView.tsx
@@ -3,6 +3,7 @@ import {View} from 'react-native';
 import {Rect} from 'react-native-svg';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
 import variables from '@styles/variables';
 import SkeletonViewContentLoader from './SkeletonViewContentLoader';
 
@@ -13,6 +14,7 @@ type TransactionPreviewSkeletonViewProps = {
 function TransactionPreviewSkeletonView({transactionPreviewWidth}: TransactionPreviewSkeletonViewProps) {
     const theme = useTheme();
     const styles = useThemeStyles();
+    useSkeletonSpan('TransactionPreviewSkeletonView');
 
     const height = variables.transactionPreviewSkeletonHeight;
     const widthOfTheLeftSkeleton = 120;
diff --git a/src/libs/telemetry/useSkeletonSpan.ts b/src/libs/telemetry/useSkeletonSpan.ts
index d6a4287c38d8..3346c9e28843 100644
--- a/src/libs/telemetry/useSkeletonSpan.ts
+++ b/src/libs/telemetry/useSkeletonSpan.ts
@@ -1,15 +1,15 @@
 /**
  * This file contains logic related to tracking skeleton across the application.
  */
-import CONST from '@src/CONST';
-import {endSpan, startSpan} from '@libs/telemetry/activeSpans';
 import {useEffect, useId, useMemo} from 'react';
+import {endSpan, startSpan} from '@libs/telemetry/activeSpans';
+import CONST from '@src/CONST';
 
 function useSkeletonSpan(component: string) {
     const reactId = useId();
 
     useEffect(() => {
-        const spanId = `${CONST.TELEMETRY.SPAN_SKELETON}_${component}_${reactId}`
+        const spanId = `${CONST.TELEMETRY.SPAN_SKELETON}_${component}_${reactId}`;
         startSpan(spanId, {
             op: CONST.TELEMETRY.SPAN_SKELETON,
             name: component,

From 67710820b809a4a3421cf28f8fd34158ab3ca079 Mon Sep 17 00:00:00 2001
From: Hubert Sosinski <hubert.sosinski@callstack.com>
Date: Sat, 29 Nov 2025 14:09:15 +0100
Subject: [PATCH 3/9] lint related fixes

---
 src/components/AccountSwitcherSkeletonView/index.tsx      | 2 +-
 src/components/AvatarSkeleton.tsx                         | 2 +-
 src/components/MoneyReportHeaderStatusBarSkeleton.tsx     | 2 +-
 .../ReportActionsListLoadingSkeleton.tsx                  | 2 +-
 src/components/OptionsListSkeletonView.tsx                | 2 +-
 src/components/ReportHeaderSkeletonView.tsx               | 2 +-
 src/components/Skeletons/CardRowSkeleton.tsx              | 2 +-
 src/components/Skeletons/MergeExpensesSkeleton.tsx        | 2 +-
 src/components/Skeletons/SearchFiltersSkeleton.tsx        | 2 +-
 src/components/Skeletons/SearchInputSelectionSkeleton.tsx | 2 +-
 src/components/Skeletons/SearchRowSkeleton.tsx            | 2 +-
 src/components/Skeletons/TabNavigatorSkeleton.tsx         | 2 +-
 src/components/Skeletons/TableRowSkeleton.tsx             | 2 +-
 src/components/Skeletons/UnreportedExpensesSkeleton.tsx   | 2 +-
 src/components/Skeletons/WorkspaceRowSkeleton.tsx         | 2 +-
 src/components/TransactionPreviewSkeletonView.tsx         | 2 +-
 src/libs/telemetry/useSkeletonSpan.ts                     | 8 ++++----
 17 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/src/components/AccountSwitcherSkeletonView/index.tsx b/src/components/AccountSwitcherSkeletonView/index.tsx
index a99315120b45..3991396eae9d 100644
--- a/src/components/AccountSwitcherSkeletonView/index.tsx
+++ b/src/components/AccountSwitcherSkeletonView/index.tsx
@@ -6,7 +6,7 @@ import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
 import useStyleUtils from '@hooks/useStyleUtils';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import CONST from '@src/CONST';
 
 type AccountSwitcherSkeletonViewProps = {
diff --git a/src/components/AvatarSkeleton.tsx b/src/components/AvatarSkeleton.tsx
index c11a57b6c677..f3bf3c156fa5 100644
--- a/src/components/AvatarSkeleton.tsx
+++ b/src/components/AvatarSkeleton.tsx
@@ -3,7 +3,7 @@ import {Circle} from 'react-native-svg';
 import type {ValueOf} from 'type-fest';
 import useStyleUtils from '@hooks/useStyleUtils';
 import useTheme from '@hooks/useTheme';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import CONST from '@src/CONST';
 import SkeletonViewContentLoader from './SkeletonViewContentLoader';
 
diff --git a/src/components/MoneyReportHeaderStatusBarSkeleton.tsx b/src/components/MoneyReportHeaderStatusBarSkeleton.tsx
index c6f88dae19a5..a410885cbd43 100644
--- a/src/components/MoneyReportHeaderStatusBarSkeleton.tsx
+++ b/src/components/MoneyReportHeaderStatusBarSkeleton.tsx
@@ -3,7 +3,7 @@ import {View} from 'react-native';
 import {Rect} from 'react-native-svg';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import SkeletonViewContentLoader from './SkeletonViewContentLoader';
 
 function MoneyReportHeaderStatusBarSkeleton() {
diff --git a/src/components/MoneyRequestReportView/ReportActionsListLoadingSkeleton.tsx b/src/components/MoneyRequestReportView/ReportActionsListLoadingSkeleton.tsx
index f7e6dd955f67..6c5d6de8c263 100644
--- a/src/components/MoneyRequestReportView/ReportActionsListLoadingSkeleton.tsx
+++ b/src/components/MoneyRequestReportView/ReportActionsListLoadingSkeleton.tsx
@@ -1,7 +1,7 @@
 import React from 'react';
 import Animated, {FadeIn, FadeOut} from 'react-native-reanimated';
 import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 
 function ReportActionsListLoadingSkeleton() {
     useSkeletonSpan('ReportActionsListLoadingSkeleton');
diff --git a/src/components/OptionsListSkeletonView.tsx b/src/components/OptionsListSkeletonView.tsx
index 0b214f7dc2b2..e1701f63f6c9 100644
--- a/src/components/OptionsListSkeletonView.tsx
+++ b/src/components/OptionsListSkeletonView.tsx
@@ -1,7 +1,7 @@
 import React from 'react';
 import {Circle, Rect} from 'react-native-svg';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import ItemListSkeletonView from './Skeletons/ItemListSkeletonView';
 
 function getLinedWidth(index: number): string {
diff --git a/src/components/ReportHeaderSkeletonView.tsx b/src/components/ReportHeaderSkeletonView.tsx
index 4df82185e890..c5b60c228da1 100644
--- a/src/components/ReportHeaderSkeletonView.tsx
+++ b/src/components/ReportHeaderSkeletonView.tsx
@@ -5,7 +5,7 @@ import useLocalize from '@hooks/useLocalize';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import CONST from '@src/CONST';
 import Icon from './Icon';
 import * as Expensicons from './Icon/Expensicons';
diff --git a/src/components/Skeletons/CardRowSkeleton.tsx b/src/components/Skeletons/CardRowSkeleton.tsx
index ead6e54eaad5..b39168a2e717 100644
--- a/src/components/Skeletons/CardRowSkeleton.tsx
+++ b/src/components/Skeletons/CardRowSkeleton.tsx
@@ -3,7 +3,7 @@ import {Circle, Rect} from 'react-native-svg';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useWindowDimensions from '@hooks/useWindowDimensions';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import variables from '@styles/variables';
 import ItemListSkeletonView from './ItemListSkeletonView';
 
diff --git a/src/components/Skeletons/MergeExpensesSkeleton.tsx b/src/components/Skeletons/MergeExpensesSkeleton.tsx
index 5b3480dd413e..b5dd1e7614e0 100644
--- a/src/components/Skeletons/MergeExpensesSkeleton.tsx
+++ b/src/components/Skeletons/MergeExpensesSkeleton.tsx
@@ -2,7 +2,7 @@ import React, {useCallback, useLayoutEffect, useRef} from 'react';
 import {View} from 'react-native';
 import {Rect} from 'react-native-svg';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import ItemListSkeletonView from './ItemListSkeletonView';
 
 const barHeight = 7;
diff --git a/src/components/Skeletons/SearchFiltersSkeleton.tsx b/src/components/Skeletons/SearchFiltersSkeleton.tsx
index 9cd6bc0c791a..1c2b5e7d926b 100644
--- a/src/components/Skeletons/SearchFiltersSkeleton.tsx
+++ b/src/components/Skeletons/SearchFiltersSkeleton.tsx
@@ -4,7 +4,7 @@ import {Rect} from 'react-native-svg';
 import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 
 type SearchFiltersSkeletonProps = {
     shouldAnimate?: boolean;
diff --git a/src/components/Skeletons/SearchInputSelectionSkeleton.tsx b/src/components/Skeletons/SearchInputSelectionSkeleton.tsx
index 084aafab162d..80a462e5c9c2 100644
--- a/src/components/Skeletons/SearchInputSelectionSkeleton.tsx
+++ b/src/components/Skeletons/SearchInputSelectionSkeleton.tsx
@@ -4,7 +4,7 @@ import {Rect} from 'react-native-svg';
 import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import variables from '@styles/variables';
 
 function SearchInputSelectionSkeleton() {
diff --git a/src/components/Skeletons/SearchRowSkeleton.tsx b/src/components/Skeletons/SearchRowSkeleton.tsx
index 231e11de780f..9bb380e18c00 100644
--- a/src/components/Skeletons/SearchRowSkeleton.tsx
+++ b/src/components/Skeletons/SearchRowSkeleton.tsx
@@ -5,7 +5,7 @@ import {Circle, Rect} from 'react-native-svg';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useWindowDimensions from '@hooks/useWindowDimensions';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import variables from '@styles/variables';
 import CONST from '@src/CONST';
 import ItemListSkeletonView from './ItemListSkeletonView';
diff --git a/src/components/Skeletons/TabNavigatorSkeleton.tsx b/src/components/Skeletons/TabNavigatorSkeleton.tsx
index b0f6affecc6a..8597c861c0f3 100644
--- a/src/components/Skeletons/TabNavigatorSkeleton.tsx
+++ b/src/components/Skeletons/TabNavigatorSkeleton.tsx
@@ -4,7 +4,7 @@ import {Rect} from 'react-native-svg';
 import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 
 function TabNavigatorSkeleton() {
     const styles = useThemeStyles();
diff --git a/src/components/Skeletons/TableRowSkeleton.tsx b/src/components/Skeletons/TableRowSkeleton.tsx
index 38e39878d9c3..cb6da8d3ee53 100644
--- a/src/components/Skeletons/TableRowSkeleton.tsx
+++ b/src/components/Skeletons/TableRowSkeleton.tsx
@@ -1,7 +1,7 @@
 import React from 'react';
 import {Circle, Rect} from 'react-native-svg';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import ItemListSkeletonView from './ItemListSkeletonView';
 
 type TableListItemSkeletonProps = {
diff --git a/src/components/Skeletons/UnreportedExpensesSkeleton.tsx b/src/components/Skeletons/UnreportedExpensesSkeleton.tsx
index 787e19353d08..a74537f91c86 100644
--- a/src/components/Skeletons/UnreportedExpensesSkeleton.tsx
+++ b/src/components/Skeletons/UnreportedExpensesSkeleton.tsx
@@ -2,7 +2,7 @@ import React, {useCallback, useLayoutEffect, useRef} from 'react';
 import {View} from 'react-native';
 import {Rect} from 'react-native-svg';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import ItemListSkeletonView from './ItemListSkeletonView';
 
 function getMessageSkeletonWidth(index: number) {
diff --git a/src/components/Skeletons/WorkspaceRowSkeleton.tsx b/src/components/Skeletons/WorkspaceRowSkeleton.tsx
index 6c00cc3bbfb0..c6d4fa465fd5 100644
--- a/src/components/Skeletons/WorkspaceRowSkeleton.tsx
+++ b/src/components/Skeletons/WorkspaceRowSkeleton.tsx
@@ -3,7 +3,7 @@ import {Rect} from 'react-native-svg';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useWindowDimensions from '@hooks/useWindowDimensions';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import variables from '@styles/variables';
 import ItemListSkeletonView from './ItemListSkeletonView';
 
diff --git a/src/components/TransactionPreviewSkeletonView.tsx b/src/components/TransactionPreviewSkeletonView.tsx
index cb3330581698..93de617d72ce 100644
--- a/src/components/TransactionPreviewSkeletonView.tsx
+++ b/src/components/TransactionPreviewSkeletonView.tsx
@@ -3,7 +3,7 @@ import {View} from 'react-native';
 import {Rect} from 'react-native-svg';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {useSkeletonSpan} from '@libs/telemetry/useSkeletonSpan';
+import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import variables from '@styles/variables';
 import SkeletonViewContentLoader from './SkeletonViewContentLoader';
 
diff --git a/src/libs/telemetry/useSkeletonSpan.ts b/src/libs/telemetry/useSkeletonSpan.ts
index 3346c9e28843..50ad55e4dcdb 100644
--- a/src/libs/telemetry/useSkeletonSpan.ts
+++ b/src/libs/telemetry/useSkeletonSpan.ts
@@ -1,9 +1,9 @@
 /**
  * This file contains logic related to tracking skeleton across the application.
  */
-import {useEffect, useId, useMemo} from 'react';
-import {endSpan, startSpan} from '@libs/telemetry/activeSpans';
+import {useEffect, useId} from 'react';
 import CONST from '@src/CONST';
+import {endSpan, startSpan} from './activeSpans';
 
 function useSkeletonSpan(component: string) {
     const reactId = useId();
@@ -16,7 +16,7 @@ function useSkeletonSpan(component: string) {
         });
 
         return () => endSpan(spanId);
-    }, []);
+    }, [component, reactId]);
 }
 
-export {useSkeletonSpan};
+export default useSkeletonSpan;

From 9dd1d7034c1b726e6ea0f9bc48b5ad6c3b7cb881 Mon Sep 17 00:00:00 2001
From: Hubert Sosinski <hubert.sosinski@callstack.com>
Date: Wed, 3 Dec 2025 14:08:18 +0100
Subject: [PATCH 4/9] Fix skeletons

---
 .../AccountSwitcherSkeletonView/index.tsx     |  7 +++---
 .../MoneyReportHeaderStatusBarSkeleton.tsx    |  6 ++---
 src/components/OptionsListSkeletonView.tsx    |  7 +++---
 src/components/ReportHeaderSkeletonView.tsx   | 15 +++++--------
 src/components/Skeletons/CardRowSkeleton.tsx  | 22 ++++++++++++-------
 .../Skeletons/MergeExpensesSkeleton.tsx       | 15 +++++--------
 .../Skeletons/SearchFiltersSkeleton.tsx       |  6 ++---
 .../SearchInputSelectionSkeleton.tsx          |  2 --
 .../Skeletons/TabNavigatorSkeleton.tsx        |  6 ++---
 src/components/Skeletons/TableRowSkeleton.tsx |  6 ++---
 .../Skeletons/UnreportedExpensesSkeleton.tsx  | 12 ++++------
 .../Skeletons/WorkspaceRowSkeleton.tsx        | 21 ++++++------------
 .../TransactionPreviewSkeletonView.tsx        | 12 ++++------
 13 files changed, 53 insertions(+), 84 deletions(-)

diff --git a/src/components/AccountSwitcherSkeletonView/index.tsx b/src/components/AccountSwitcherSkeletonView/index.tsx
index 3991396eae9d..4e783be4fc8a 100644
--- a/src/components/AccountSwitcherSkeletonView/index.tsx
+++ b/src/components/AccountSwitcherSkeletonView/index.tsx
@@ -25,6 +25,7 @@ function AccountSwitcherSkeletonView({shouldAnimate = true, avatarSize = CONST.A
     const avatarPlaceholderSize = StyleUtils.getAvatarSize(avatarSize);
     const avatarPlaceholderRadius = avatarPlaceholderSize / 2;
     const startPositionX = avatarPlaceholderRadius;
+    const rectXTranslation = startPositionX + avatarPlaceholderRadius + styles.gap3.gap;
 
     return (
         <View style={styles.avatarSectionWrapperSkeleton}>
@@ -40,14 +41,12 @@ function AccountSwitcherSkeletonView({shouldAnimate = true, avatarSize = CONST.A
                     r={avatarPlaceholderRadius}
                 />
                 <Rect
-                    x={startPositionX + avatarPlaceholderRadius + styles.gap3.gap}
-                    y="6"
+                    transform={[{translateX: rectXTranslation}, {translateY: 6}]}
                     width="45%"
                     height="8"
                 />
                 <Rect
-                    x={startPositionX + avatarPlaceholderRadius + styles.gap3.gap}
-                    y="26"
+                    transform={[{translateX: rectXTranslation}, {translateY: 26}]}
                     width="55%"
                     height="8"
                 />
diff --git a/src/components/MoneyReportHeaderStatusBarSkeleton.tsx b/src/components/MoneyReportHeaderStatusBarSkeleton.tsx
index a410885cbd43..63ec9573e0bb 100644
--- a/src/components/MoneyReportHeaderStatusBarSkeleton.tsx
+++ b/src/components/MoneyReportHeaderStatusBarSkeleton.tsx
@@ -19,14 +19,12 @@ function MoneyReportHeaderStatusBarSkeleton() {
                 foregroundColor={theme.skeletonLHNOut}
             >
                 <Rect
-                    x={0}
-                    y={12}
+                    transform={[{translateY: 12}]}
                     width={16}
                     height={8}
                 />
                 <Rect
-                    x={24}
-                    y={12}
+                    transform={[{translateX: 24}, {translateY: 12}]}
                     width={120}
                     height={8}
                 />
diff --git a/src/components/OptionsListSkeletonView.tsx b/src/components/OptionsListSkeletonView.tsx
index e1701f63f6c9..00af27f457da 100644
--- a/src/components/OptionsListSkeletonView.tsx
+++ b/src/components/OptionsListSkeletonView.tsx
@@ -39,6 +39,7 @@ function OptionsListSkeletonView({shouldAnimate = true, shouldStyleAsTable = fal
             gradientOpacityEnabled={gradientOpacityEnabled}
             renderSkeletonItem={({itemIndex}) => {
                 const lineWidth = getLinedWidth(itemIndex);
+                const textStartX = shouldStyleAsTable ? '68' : '72';
 
                 return (
                     <>
@@ -48,14 +49,12 @@ function OptionsListSkeletonView({shouldAnimate = true, shouldStyleAsTable = fal
                             r="20"
                         />
                         <Rect
-                            x={shouldStyleAsTable ? '68' : '72'}
-                            y="18"
+                            transform={[{translateX: textStartX}, {translateY: 18}]}
                             width="20%"
                             height="8"
                         />
                         <Rect
-                            x={shouldStyleAsTable ? '68' : '72'}
-                            y="38"
+                            transform={[{translateX: textStartX}, {translateY: 38}]}
                             width={shouldStyleAsTable ? '10%' : lineWidth}
                             height="8"
                         />
diff --git a/src/components/ReportHeaderSkeletonView.tsx b/src/components/ReportHeaderSkeletonView.tsx
index c5b60c228da1..efc0194bd4ad 100644
--- a/src/components/ReportHeaderSkeletonView.tsx
+++ b/src/components/ReportHeaderSkeletonView.tsx
@@ -1,6 +1,7 @@
 import React from 'react';
 import {View} from 'react-native';
 import {Circle, Rect} from 'react-native-svg';
+import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useTheme from '@hooks/useTheme';
@@ -8,7 +9,6 @@ import useThemeStyles from '@hooks/useThemeStyles';
 import useSkeletonSpan from '@libs/telemetry/useSkeletonSpan';
 import CONST from '@src/CONST';
 import Icon from './Icon';
-import * as Expensicons from './Icon/Expensicons';
 import PressableWithFeedback from './Pressable/PressableWithFeedback';
 import SkeletonViewContentLoader from './SkeletonViewContentLoader';
 
@@ -22,6 +22,7 @@ function ReportHeaderSkeletonView({shouldAnimate = true, onBackButtonPress = ()
     const styles = useThemeStyles();
     const {translate} = useLocalize();
     const {shouldUseNarrowLayout} = useResponsiveLayout();
+    const icons = useMemoizedLazyExpensifyIcons(['BackArrow'] as const);
     useSkeletonSpan('ReportHeaderSkeletonView');
     const height = styles.headerBarHeight.height;
     const radius = 20;
@@ -41,7 +42,7 @@ function ReportHeaderSkeletonView({shouldAnimate = true, onBackButtonPress = ()
                     >
                         <Icon
                             fill={theme.icon}
-                            src={Expensicons.BackArrow}
+                            src={icons.BackArrow}
                         />
                     </PressableWithFeedback>
                 )}
@@ -58,18 +59,12 @@ function ReportHeaderSkeletonView({shouldAnimate = true, onBackButtonPress = ()
                         r={radius}
                     />
                     <Rect
-                        // eslint-disable-next-line @typescript-eslint/no-deprecated
-                        x="55"
-                        // eslint-disable-next-line @typescript-eslint/no-deprecated
-                        y={circleTopY + 8}
+                        transform={[{translateX: 55}, {translateY: circleTopY + 8}]}
                         width="30%"
                         height="8"
                     />
                     <Rect
-                        // eslint-disable-next-line @typescript-eslint/no-deprecated
-                        x="55"
-                        // eslint-disable-next-line @typescript-eslint/no-deprecated
-                        y={circleBottomY - 12}
+                        transform={[{translateX: 55}, {translateY: circleBottomY - 12}]}
                         width="40%"
                         height="8"
                     />
diff --git a/src/components/Skeletons/CardRowSkeleton.tsx b/src/components/Skeletons/CardRowSkeleton.tsx
index b39168a2e717..76bd02311eab 100644
--- a/src/components/Skeletons/CardRowSkeleton.tsx
+++ b/src/components/Skeletons/CardRowSkeleton.tsx
@@ -42,15 +42,13 @@ function CardRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEn
                         r={20}
                     />
                     <Rect
-                        x={66}
-                        y={22}
+                        transform={[{translateX: 66}, {translateY: 22}]}
                         width={longBarWidth}
                         height={barHeight}
                     />
 
                     <Rect
-                        x={66}
-                        y={36}
+                        transform={[{translateX: 66}, {translateY: 36}]}
                         width={shortBarWidth}
                         height={barHeight}
                     />
@@ -59,16 +57,24 @@ function CardRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEn
                         <>
                             <Rect
                                 // We have to calculate this value to make sure the element is aligned to the button on the right side.
-                                x={windowWidth - leftPaneWidth - rightButtonWidth - gapWidth - centralPanePadding - gapWidth - rightSideElementWidth}
-                                y={28}
+                                transform={[
+                                    {
+                                        translateX: windowWidth - leftPaneWidth - rightButtonWidth - gapWidth - centralPanePadding - gapWidth - rightSideElementWidth,
+                                    },
+                                    {translateY: 28},
+                                ]}
                                 width={20}
                                 height={barHeight}
                             />
 
                             <Rect
                                 // We have to calculate this value to make sure the element is aligned to the right border.
-                                x={windowWidth - leftPaneWidth - rightSideElementWidth - gapWidth - centralPanePadding}
-                                y={28}
+                                transform={[
+                                    {
+                                        translateX: windowWidth - leftPaneWidth - rightSideElementWidth - gapWidth - centralPanePadding,
+                                    },
+                                    {translateY: 28},
+                                ]}
                                 width={50}
                                 height={barHeight}
                             />
diff --git a/src/components/Skeletons/MergeExpensesSkeleton.tsx b/src/components/Skeletons/MergeExpensesSkeleton.tsx
index b5dd1e7614e0..6fce1cad7cb1 100644
--- a/src/components/Skeletons/MergeExpensesSkeleton.tsx
+++ b/src/components/Skeletons/MergeExpensesSkeleton.tsx
@@ -30,39 +30,34 @@ function MergeExpensesSkeleton({fixedNumItems, speed}: MergeExpensesSkeletonProp
         return (
             <>
                 <Rect
-                    x={12}
-                    y={12}
+                    transform={[{translateX: 12}, {translateY: 12}]}
                     width={36}
                     height={40}
                     rx={4}
                     ry={4}
                 />
                 <Rect
-                    x={66}
-                    y={22}
+                    transform={[{translateX: 66}, {translateY: 22}]}
                     width={longBarWidth}
                     height={barHeight}
                 />
 
                 <Rect
-                    x={66}
-                    y={36}
+                    transform={[{translateX: 66}, {translateY: 36}]}
                     width={mediumBarWidth}
                     height={barHeight}
                 />
 
                 <Rect
                     // We have to calculate this value to make sure the element is aligned to the right border.
-                    x={pageWidth - 12 - mediumBarWidth}
-                    y={22}
+                    transform={[{translateX: pageWidth - 12 - mediumBarWidth}, {translateY: 22}]}
                     width={mediumBarWidth}
                     height={barHeight}
                 />
 
                 <Rect
                     // We have to calculate this value to make sure the element is aligned to the right border.
-                    x={pageWidth - 12 - shortBarWidth}
-                    y={36}
+                    transform={[{translateX: pageWidth - 12 - shortBarWidth}, {translateY: 36}]}
                     width={shortBarWidth}
                     height={barHeight}
                 />
diff --git a/src/components/Skeletons/SearchFiltersSkeleton.tsx b/src/components/Skeletons/SearchFiltersSkeleton.tsx
index 1c2b5e7d926b..dacf34739b92 100644
--- a/src/components/Skeletons/SearchFiltersSkeleton.tsx
+++ b/src/components/Skeletons/SearchFiltersSkeleton.tsx
@@ -29,8 +29,7 @@ function SearchFiltersSkeleton({shouldAnimate = true}: SearchFiltersSkeletonProp
                     <Rect
                         // eslint-disable-next-line react/no-array-index-key
                         key={index}
-                        x={index * 90}
-                        y={0}
+                        transform={[{translateX: index * 90}]}
                         rx={14}
                         ry={14}
                         width={84}
@@ -50,8 +49,7 @@ function SearchFiltersSkeleton({shouldAnimate = true}: SearchFiltersSkeletonProp
                         <Rect
                             // eslint-disable-next-line react/no-array-index-key
                             key={index}
-                            x={12 + index * 90}
-                            y={10}
+                            transform={[{translateX: 12 + index * 90}, {translateY: 10}]}
                             width={60}
                             height={8}
                         />
diff --git a/src/components/Skeletons/SearchInputSelectionSkeleton.tsx b/src/components/Skeletons/SearchInputSelectionSkeleton.tsx
index 80a462e5c9c2..f1c9f54bd5ba 100644
--- a/src/components/Skeletons/SearchInputSelectionSkeleton.tsx
+++ b/src/components/Skeletons/SearchInputSelectionSkeleton.tsx
@@ -21,8 +21,6 @@ function SearchInputSelectionSkeleton() {
                 style={[styles.ml1]}
             >
                 <Rect
-                    x="0"
-                    y="0"
                     width={variables.searchAutocompleteInputSkeletonWidth}
                     height={variables.searchAutocompleteInputSkeletonHeight}
                 />
diff --git a/src/components/Skeletons/TabNavigatorSkeleton.tsx b/src/components/Skeletons/TabNavigatorSkeleton.tsx
index 8597c861c0f3..ca04b4e9b094 100644
--- a/src/components/Skeletons/TabNavigatorSkeleton.tsx
+++ b/src/components/Skeletons/TabNavigatorSkeleton.tsx
@@ -21,8 +21,7 @@ function TabNavigatorSkeleton() {
                 style={[styles.flex1, styles.ml4, styles.button, styles.highlightBG]}
             >
                 <Rect
-                    x="20%"
-                    y={13}
+                    transform={[{translateX: '20%'}, {translateY: 13}]}
                     width="60%"
                     height={14}
                 />
@@ -35,8 +34,7 @@ function TabNavigatorSkeleton() {
                 style={[styles.flex1, styles.mr4, styles.button, styles.appBG]}
             >
                 <Rect
-                    x="20%"
-                    y={13}
+                    transform={[{translateX: '20%'}, {translateY: 13}]}
                     width="60%"
                     height={14}
                 />
diff --git a/src/components/Skeletons/TableRowSkeleton.tsx b/src/components/Skeletons/TableRowSkeleton.tsx
index cb6da8d3ee53..c504472ecb7c 100644
--- a/src/components/Skeletons/TableRowSkeleton.tsx
+++ b/src/components/Skeletons/TableRowSkeleton.tsx
@@ -32,14 +32,12 @@ function TableListItemSkeleton({shouldAnimate = true, fixedNumItems, gradientOpa
                         r="20"
                     />
                     <Rect
-                        x="80"
-                        y="20"
+                        transform={[{translateX: 80}, {translateY: 20}]}
                         width={longBarWidth}
                         height={barHeight}
                     />
                     <Rect
-                        x="80"
-                        y="36"
+                        transform={[{translateX: 80}, {translateY: 36}]}
                         width={shortBarWidth}
                         height={barHeight}
                     />
diff --git a/src/components/Skeletons/UnreportedExpensesSkeleton.tsx b/src/components/Skeletons/UnreportedExpensesSkeleton.tsx
index a74537f91c86..545584ab196a 100644
--- a/src/components/Skeletons/UnreportedExpensesSkeleton.tsx
+++ b/src/components/Skeletons/UnreportedExpensesSkeleton.tsx
@@ -47,30 +47,26 @@ function UnreportedExpensesSkeleton({fixedNumberOfItems}: {fixedNumberOfItems?:
             return (
                 <>
                     <Rect
-                        x={12}
-                        y={22}
+                        transform={[{translateX: 12}, {translateY: 22}]}
                         width={20}
                         height={20}
                         rx={4}
                         ry={4}
                     />
                     <Rect
-                        x={44}
-                        y={12}
+                        transform={[{translateX: 44}, {translateY: 12}]}
                         width={36}
                         height={40}
                         rx={4}
                         ry={4}
                     />
                     <Rect
-                        x={92}
-                        y={26}
+                        transform={[{translateX: 92}, {translateY: 26}]}
                         width={getMessageSkeletonWidth(args.itemIndex)}
                         height={12}
                     />
                     <Rect
-                        x={pageWidth - 12 - getExpenseAmountSkeletonWidth(args.itemIndex)}
-                        y={26}
+                        transform={[{translateX: pageWidth - 12 - getExpenseAmountSkeletonWidth(args.itemIndex)}, {translateY: 26}]}
                         width={getExpenseAmountSkeletonWidth(args.itemIndex)}
                         height={12}
                     />
diff --git a/src/components/Skeletons/WorkspaceRowSkeleton.tsx b/src/components/Skeletons/WorkspaceRowSkeleton.tsx
index c6d4fa465fd5..a5be700aa9aa 100644
--- a/src/components/Skeletons/WorkspaceRowSkeleton.tsx
+++ b/src/components/Skeletons/WorkspaceRowSkeleton.tsx
@@ -36,48 +36,41 @@ function WorkspaceRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpac
             renderSkeletonItem={() => (
                 <>
                     <Rect
-                        x={12}
-                        y={12}
+                        transform={[{translateX: 12}, {translateY: 12}]}
                         rx={5}
                         ry={5}
                         width={36}
                         height={40}
                     />
                     <Rect
-                        x={66}
-                        y={22}
+                        transform={[{translateX: 66}, {translateY: 22}]}
                         width={longBarWidth}
                         height={barHeight}
                     />
                     <Rect
-                        x={66}
-                        y={36}
+                        transform={[{translateX: 66}, {translateY: 36}]}
                         width={shortBarWidth}
                         height={barHeight}
                     />
                     {!shouldUseNarrowLayout && (
                         <>
                             <Rect
-                                x={66 + partWidth}
-                                y={22}
+                                transform={[{translateX: 66 + partWidth}, {translateY: 22}]}
                                 width={longBarWidth}
                                 height={barHeight}
                             />
                             <Rect
-                                x={66 + partWidth}
-                                y={36}
+                                transform={[{translateX: 66 + partWidth}, {translateY: 36}]}
                                 width={shortBarWidth}
                                 height={barHeight}
                             />
                             <Rect
-                                x={66 + partWidth * 2}
-                                y={22}
+                                transform={[{translateX: 66 + partWidth * 2}, {translateY: 22}]}
                                 width={longBarWidth}
                                 height={barHeight}
                             />
                             <Rect
-                                x={66 + partWidth * 2}
-                                y={36}
+                                transform={[{translateX: 66 + partWidth * 2}, {translateY: 36}]}
                                 width={shortBarWidth}
                                 height={barHeight}
                             />
diff --git a/src/components/TransactionPreviewSkeletonView.tsx b/src/components/TransactionPreviewSkeletonView.tsx
index 93de617d72ce..57366081aee5 100644
--- a/src/components/TransactionPreviewSkeletonView.tsx
+++ b/src/components/TransactionPreviewSkeletonView.tsx
@@ -31,20 +31,17 @@ function TransactionPreviewSkeletonView({transactionPreviewWidth}: TransactionPr
                 foregroundColor={theme.skeletonLHNOut}
             >
                 <Rect
-                    x="0"
-                    y="4"
+                    transform={[{translateY: 4}]}
                     width="64"
                     height="8"
                 />
                 <Rect
-                    x="0"
-                    y="24"
+                    transform={[{translateY: 24}]}
                     width={widthOfTheLeftSkeleton}
                     height="20"
                 />
                 <Rect
-                    x="0"
-                    y="54.75"
+                    transform={[{translateY: 54.75}]}
                     width="80"
                     height="7"
                 />
@@ -57,8 +54,7 @@ function TransactionPreviewSkeletonView({transactionPreviewWidth}: TransactionPr
                     backgroundColor={theme.skeletonLHNIn}
                 >
                     <Rect
-                        x="0"
-                        y="24"
+                        transform={[{translateY: 24}]}
                         width={widthOfTheRightSkeleton}
                         height="20"
                     />

From fedada1e66bcd2b7e67fc64e7d7e536b9979c682 Mon Sep 17 00:00:00 2001
From: Hubert Sosinski <hubert.sosinski@callstack.com>
Date: Wed, 3 Dec 2025 14:29:49 +0100
Subject: [PATCH 5/9] Fixes after AI review

---
 src/libs/telemetry/useSkeletonSpan.ts | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/libs/telemetry/useSkeletonSpan.ts b/src/libs/telemetry/useSkeletonSpan.ts
index 50ad55e4dcdb..0c50ce32cadd 100644
--- a/src/libs/telemetry/useSkeletonSpan.ts
+++ b/src/libs/telemetry/useSkeletonSpan.ts
@@ -1,11 +1,11 @@
-/**
- * This file contains logic related to tracking skeleton across the application.
- */
 import {useEffect, useId} from 'react';
 import CONST from '@src/CONST';
 import {endSpan, startSpan} from './activeSpans';
 
-function useSkeletonSpan(component: string) {
+/**
+ * Create a span for a skeleton component. This helps identify "infinite skeleton" issues where loading states don't resolve.
+ */
+function useSkeletonSpan(component: string ) {
     const reactId = useId();
 
     useEffect(() => {

From 446f1093c77c8290840f7c3f6176188315c8527e Mon Sep 17 00:00:00 2001
From: Hubert Sosinski <hubert.sosinski@callstack.com>
Date: Wed, 3 Dec 2025 14:37:47 +0100
Subject: [PATCH 6/9] pretty fix for prettier

---
 src/libs/telemetry/useSkeletonSpan.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/telemetry/useSkeletonSpan.ts b/src/libs/telemetry/useSkeletonSpan.ts
index 0c50ce32cadd..84e9948673a3 100644
--- a/src/libs/telemetry/useSkeletonSpan.ts
+++ b/src/libs/telemetry/useSkeletonSpan.ts
@@ -5,7 +5,7 @@ import {endSpan, startSpan} from './activeSpans';
 /**
  * Create a span for a skeleton component. This helps identify "infinite skeleton" issues where loading states don't resolve.
  */
-function useSkeletonSpan(component: string ) {
+function useSkeletonSpan(component: string) {
     const reactId = useId();
 
     useEffect(() => {

From 0624c0778a2f291190eae89f569844a96d59e124 Mon Sep 17 00:00:00 2001
From: Hubert Sosinski <hubert.sosinski@callstack.com>
Date: Thu, 4 Dec 2025 08:31:52 +0100
Subject: [PATCH 7/9] Fix typecheck

---
 src/components/OptionsListSkeletonView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/OptionsListSkeletonView.tsx b/src/components/OptionsListSkeletonView.tsx
index 00af27f457da..c07e5cb2e3a9 100644
--- a/src/components/OptionsListSkeletonView.tsx
+++ b/src/components/OptionsListSkeletonView.tsx
@@ -39,7 +39,7 @@ function OptionsListSkeletonView({shouldAnimate = true, shouldStyleAsTable = fal
             gradientOpacityEnabled={gradientOpacityEnabled}
             renderSkeletonItem={({itemIndex}) => {
                 const lineWidth = getLinedWidth(itemIndex);
-                const textStartX = shouldStyleAsTable ? '68' : '72';
+                const textStartX = shouldStyleAsTable ? 68 : 72;
 
                 return (
                     <>

From cba82f8defb32665b8eaa38493c9fcba0141f7ec Mon Sep 17 00:00:00 2001
From: Hubert Sosinski <hubert.sosinski@callstack.com>
Date: Fri, 5 Dec 2025 13:55:07 +0100
Subject: [PATCH 8/9] Introduce minDurationFilter.ts

---
 src/CONST/index.ts                            |  5 ++++
 src/libs/telemetry/activeSpans.ts             | 22 ++++++++++++----
 src/libs/telemetry/middlewares/index.ts       |  3 ++-
 .../middlewares/minDurationFilter.ts          | 25 +++++++++++++++++++
 src/libs/telemetry/useSkeletonSpan.ts         | 14 ++++++++---
 5 files changed, 59 insertions(+), 10 deletions(-)
 create mode 100644 src/libs/telemetry/middlewares/minDurationFilter.ts

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 64a9ca7ce53a..1a70c3524078 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -1700,6 +1700,11 @@ const CONST = {
         ATTRIBUTE_CANCELED: 'canceled',
         ATTRIBUTE_ROUTE_FROM: 'route_from',
         ATTRIBUTE_ROUTE_TO: 'route_to',
+        ATTRIBUTE_MIN_DURATION: 'min_duration',
+        ATTRIBUTE_FINISHED_MANUALLY: 'finished_manually',
+        CONFIG: {
+            SKELETON_MIN_DURATION: 10_000,
+        },
     },
     PRIORITY_MODE: {
         GSD: 'gsd',
diff --git a/src/libs/telemetry/activeSpans.ts b/src/libs/telemetry/activeSpans.ts
index d429a365b1f5..41ae7de8b917 100644
--- a/src/libs/telemetry/activeSpans.ts
+++ b/src/libs/telemetry/activeSpans.ts
@@ -4,15 +4,26 @@ import CONST from '@src/CONST';
 
 const activeSpans = new Map<string, ReturnType<typeof Sentry.startInactiveSpan>>();
 
-function startSpan(spanId: string, options: StartSpanOptions) {
+type StartSpanExtraOptions = Partial<{
+    /**
+     * Minimum duration of the span in milliseconds. If the span is shorter than this duration, it will be discarded (filtered out) before sending to Sentry.
+     *
+     */
+    minDuration: number;
+}>;
+
+function startSpan(spanId: string, options: StartSpanOptions, extraOptions: StartSpanExtraOptions = {}): ReturnType<typeof Sentry.startInactiveSpan> | undefined {
     // End any existing span for this name
     cancelSpan(spanId);
-
     const span = Sentry.startInactiveSpan(options);
 
-    if (span) {
-        activeSpans.set(spanId, span);
+    if (!span) {
+        return;
+    }
+    if (extraOptions.minDuration) {
+        span.setAttribute(CONST.TELEMETRY.ATTRIBUTE_MIN_DURATION, extraOptions.minDuration);
     }
+    activeSpans.set(spanId, span);
 
     return span;
 }
@@ -24,6 +35,7 @@ function endSpan(spanId: string) {
         return;
     }
     span.setStatus({code: 1});
+    span.setAttribute(CONST.TELEMETRY.ATTRIBUTE_FINISHED_MANUALLY, true);
     span.end();
     activeSpans.delete(spanId);
 }
@@ -32,7 +44,7 @@ function cancelSpan(spanId: string) {
     const span = activeSpans.get(spanId);
     span?.setAttribute(CONST.TELEMETRY.ATTRIBUTE_CANCELED, true);
     // In Sentry there are only OK or ERROR status codes.
-    // We treat canceled spans as OK so we have ability to properly track spans that are not finished at all (their status would be different)
+    // We treat canceled spans as OK, so we can properly track spans that are not finished at all (their status would be different)
     span?.setStatus({code: 1});
     endSpan(spanId);
 }
diff --git a/src/libs/telemetry/middlewares/index.ts b/src/libs/telemetry/middlewares/index.ts
index 4f4f0ae3fbf6..290a2855995e 100644
--- a/src/libs/telemetry/middlewares/index.ts
+++ b/src/libs/telemetry/middlewares/index.ts
@@ -1,10 +1,11 @@
 import type {EventHint, TransactionEvent} from '@sentry/core';
 import emailDomainFilter from './emailDomainFilter';
 import firebasePerformanceFilter from './firebasePerformanceFilter';
+import minDurationFilter from './minDurationFilter';
 
 type TelemetryBeforeSend = (event: TransactionEvent, hint: EventHint) => TransactionEvent | null | Promise<TransactionEvent | null>;
 
-const middlewares: TelemetryBeforeSend[] = [emailDomainFilter, firebasePerformanceFilter];
+const middlewares: TelemetryBeforeSend[] = [emailDomainFilter, firebasePerformanceFilter, minDurationFilter];
 
 function processBeforeSendTransactions(event: TransactionEvent, hint: EventHint): Promise<TransactionEvent | null> {
     return middlewares.reduce(
diff --git a/src/libs/telemetry/middlewares/minDurationFilter.ts b/src/libs/telemetry/middlewares/minDurationFilter.ts
new file mode 100644
index 000000000000..5ce82f1c3b52
--- /dev/null
+++ b/src/libs/telemetry/middlewares/minDurationFilter.ts
@@ -0,0 +1,25 @@
+import CONST from '@src/CONST';
+import type {TelemetryBeforeSend} from './index';
+
+const minDurationFilter: TelemetryBeforeSend = (event) => {
+    if (!event.spans) {
+        return event;
+    }
+
+    const spans = event.spans.filter((span) => {
+        const minDuration = span.data?.[CONST.TELEMETRY.ATTRIBUTE_MIN_DURATION];
+        if (!minDuration || typeof minDuration !== 'number' || Number.isNaN(minDuration)) {
+            return true;
+        }
+        if (!span.timestamp) {
+            return true;
+        }
+
+        const duration = (span.timestamp - span.start_timestamp) * 1000;
+
+        return duration >= minDuration;
+    });
+    return {...event, spans};
+};
+
+export default minDurationFilter;
diff --git a/src/libs/telemetry/useSkeletonSpan.ts b/src/libs/telemetry/useSkeletonSpan.ts
index 84e9948673a3..83cdcee604db 100644
--- a/src/libs/telemetry/useSkeletonSpan.ts
+++ b/src/libs/telemetry/useSkeletonSpan.ts
@@ -10,10 +10,16 @@ function useSkeletonSpan(component: string) {
 
     useEffect(() => {
         const spanId = `${CONST.TELEMETRY.SPAN_SKELETON}_${component}_${reactId}`;
-        startSpan(spanId, {
-            op: CONST.TELEMETRY.SPAN_SKELETON,
-            name: component,
-        });
+        startSpan(
+            spanId,
+            {
+                op: CONST.TELEMETRY.SPAN_SKELETON,
+                name: component,
+            },
+            {
+                minDuration: CONST.TELEMETRY.CONFIG.SKELETON_MIN_DURATION,
+            },
+        );
 
         return () => endSpan(spanId);
     }, [component, reactId]);

From 550af94e7916179eea6a1c7d6c2d9fd098808b0e Mon Sep 17 00:00:00 2001
From: Hubert Sosinski <hubert.sosinski@callstack.com>
Date: Fri, 5 Dec 2025 14:20:01 +0100
Subject: [PATCH 9/9] Introduce minDurationFilter.ts

---
 src/libs/telemetry/activeSpans.ts | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/src/libs/telemetry/activeSpans.ts b/src/libs/telemetry/activeSpans.ts
index 41ae7de8b917..8ccf60816570 100644
--- a/src/libs/telemetry/activeSpans.ts
+++ b/src/libs/telemetry/activeSpans.ts
@@ -12,14 +12,11 @@ type StartSpanExtraOptions = Partial<{
     minDuration: number;
 }>;
 
-function startSpan(spanId: string, options: StartSpanOptions, extraOptions: StartSpanExtraOptions = {}): ReturnType<typeof Sentry.startInactiveSpan> | undefined {
+function startSpan(spanId: string, options: StartSpanOptions, extraOptions: StartSpanExtraOptions = {}) {
     // End any existing span for this name
     cancelSpan(spanId);
     const span = Sentry.startInactiveSpan(options);
 
-    if (!span) {
-        return;
-    }
     if (extraOptions.minDuration) {
         span.setAttribute(CONST.TELEMETRY.ATTRIBUTE_MIN_DURATION, extraOptions.minDuration);
     }
