Developing a Wishlist in Mobile Apps
Wishlist is a feature that's underestimated. Add a product, close the app, reopen from a different phone a week later — the list should be there. A simple task turns into a synchronization problem, conflict management, and optimistic UI.
Where to store: locally or in the cloud
Depends on authentication requirements:
-
Authenticated users only: Firestore, PostgreSQL, any server database. List bound to
userId. - Guest users + sync on registration: AsyncStorage / MMKV for guests, on login — merge with server.
Merge variant is more complex. Guest could add 10 products, and their new account already has 5 (import from another service). Merge strategy: union of two sets, no duplicates by productId.
Optimistic UI
The wishlist button should respond instantly — without waiting for server response. Common mistake: set loading on click and block the button for request duration. User sees delay and thinks they didn't click.
const useWishlist = () => {
const [wishlistIds, setWishlistIds] = useAtom(wishlistAtom);
const toggle = useCallback(async (productId: string) => {
const isAdding = !wishlistIds.has(productId);
// Immediate UI change
setWishlistIds(prev => {
const next = new Set(prev);
isAdding ? next.add(productId) : next.delete(productId);
return next;
});
try {
if (isAdding) {
await api.wishlist.add(productId);
} else {
await api.wishlist.remove(productId);
}
} catch {
// Rollback on error
setWishlistIds(prev => {
const next = new Set(prev);
isAdding ? next.delete(productId) : next.add(productId);
return next;
});
Toast.show('Failed to update wishlist');
}
}, [wishlistIds]);
return { wishlistIds, toggle };
};
MMKV for local cache
AsyncStorage is slow. For wishlist read on every product card render, use MMKV (react-native-mmkv). Synchronous read — no await:
import { MMKV } from 'react-native-mmkv';
const storage = new MMKV({ id: 'wishlist' });
const getLocalWishlist = (): Set<string> => {
const raw = storage.getString('ids');
return raw ? new Set(JSON.parse(raw)) : new Set();
};
const saveLocalWishlist = (ids: Set<string>) => {
storage.set('ids', JSON.stringify([...ids]));
};
Synchronous read from MMKV on main thread is safe, operation takes <0.1 ms.
Wishlist count in icon badge
Badge with number of elements in wishlist is derived from wishlistIds.size. Don't make a separate request for the counter. If wishlist is synchronized — the set size is already known.
Timeline
Wishlist with optimistic UI, MMKV cache, and cloud sync (Firestore or REST): 1–2 weeks.







