diff --git a/.Jules/changelog.md b/.Jules/changelog.md
index 11fc864..c91381d 100644
--- a/.Jules/changelog.md
+++ b/.Jules/changelog.md
@@ -123,3 +123,8 @@
- `.jules/todo.md`
- `.jules/knowledge.md`
- `.jules/changelog.md`
+
+### Mobile
+- Added reusable animated `Skeleton` primitive component in `mobile/components/ui/Skeleton.js`.
+- Created `HomeScreenSkeleton` loading view for groups list.
+- Replaced basic `ActivityIndicator` in `HomeScreen.js` with new skeleton loading experience.
diff --git a/.Jules/knowledge.md b/.Jules/knowledge.md
index 43a9ab0..acbdef9 100644
--- a/.Jules/knowledge.md
+++ b/.Jules/knowledge.md
@@ -756,3 +756,6 @@ _Document errors and their solutions here as you encounter them._
- react-native-paper: UI components
- axios: API calls (via api/client.js)
- expo: Platform SDK
+- When using `Animated.loop` within a `useEffect` hook in React Native components, assign the loop to a variable and explicitly call `.stop()` on it in the cleanup function to prevent memory leaks.
+- Playwright in the testing environment cannot be run with `headless=False`; attempting to do so will result in an 'Executable doesn't support UI mode' error.
+- Always clean up temporary Playwright verification scripts, logs, and generated screenshots before committing changes to maintain repository hygiene.
diff --git a/.Jules/todo.md b/.Jules/todo.md
index ebb0c7a..40fe0c3 100644
--- a/.Jules/todo.md
+++ b/.Jules/todo.md
@@ -57,8 +57,9 @@
- Impact: Native feel, users can easily refresh data
- Size: ~150 lines
-- [ ] **[ux]** Complete skeleton loading for HomeScreen groups
- - File: `mobile/screens/HomeScreen.js`
+- [x] **[ux]** Complete skeleton loading for HomeScreen groups
+ - Completed: 2026-02-14
+ - Files modified: `mobile/screens/HomeScreen.js`, `mobile/components/skeletons/HomeScreenSkeleton.js`, `mobile/components/ui/Skeleton.js`
- Context: Replace ActivityIndicator with skeleton group cards
- Impact: Better loading experience, less jarring
- Size: ~40 lines
diff --git a/mobile/components/skeletons/HomeScreenSkeleton.js b/mobile/components/skeletons/HomeScreenSkeleton.js
new file mode 100644
index 0000000..af398ac
--- /dev/null
+++ b/mobile/components/skeletons/HomeScreenSkeleton.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import { View, StyleSheet } from 'react-native';
+import { Card } from 'react-native-paper';
+import Skeleton from '../ui/Skeleton';
+
+const HomeScreenSkeleton = () => {
+ return (
+
+ {Array.from({ length: 5 }).map((_, i) => (
+
+ }
+ left={() => }
+ />
+
+
+
+
+ ))}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ padding: 16,
+ },
+ card: {
+ marginBottom: 16,
+ },
+ contentSkeleton: {
+ marginTop: 4,
+ },
+});
+
+export default HomeScreenSkeleton;
diff --git a/mobile/components/ui/Skeleton.js b/mobile/components/ui/Skeleton.js
new file mode 100644
index 0000000..f9973af
--- /dev/null
+++ b/mobile/components/ui/Skeleton.js
@@ -0,0 +1,45 @@
+import React, { useEffect, useRef } from 'react';
+import { Animated, View, StyleSheet } from 'react-native';
+import { useTheme } from 'react-native-paper';
+
+const Skeleton = ({ width, height, borderRadius = 4, style }) => {
+ const theme = useTheme();
+ const opacityAnim = useRef(new Animated.Value(0.3)).current;
+
+ useEffect(() => {
+ const loop = Animated.loop(
+ Animated.sequence([
+ Animated.timing(opacityAnim, {
+ toValue: 1,
+ duration: 700,
+ useNativeDriver: true,
+ }),
+ Animated.timing(opacityAnim, {
+ toValue: 0.3,
+ duration: 700,
+ useNativeDriver: true,
+ }),
+ ])
+ );
+ loop.start();
+
+ return () => loop.stop();
+ }, [opacityAnim]);
+
+ return (
+
+ );
+};
+
+export default Skeleton;
diff --git a/mobile/screens/HomeScreen.js b/mobile/screens/HomeScreen.js
index d2f3c38..3bca9d0 100644
--- a/mobile/screens/HomeScreen.js
+++ b/mobile/screens/HomeScreen.js
@@ -17,6 +17,7 @@ import * as Haptics from "expo-haptics";
import { createGroup, getGroups, getOptimizedSettlements } from "../api/groups";
import { AuthContext } from "../context/AuthContext";
import { formatCurrency, getCurrencySymbol } from "../utils/currency";
+import HomeScreenSkeleton from "../components/skeletons/HomeScreenSkeleton";
const HomeScreen = ({ navigation }) => {
const { token, logout, user } = useContext(AuthContext);
@@ -257,9 +258,7 @@ const HomeScreen = ({ navigation }) => {
{isLoading ? (
-
-
-
+
) : (