npx create-expo-app --template blank-typescript
type ContactListItemProps = {contact?: ContactInfo | null;};const ContactListItem: React.FC<ContactListItemProps> = ({ contact }) => {return (<Animated.View style={styles.container} entering={FadeIn.duration(1000)}><View style={styles.circleContainer}><Text style={styles.avatarText}>{contact.name?.[0]}</Text></View><View style={styles.content}><Animated.Text entering={FadeIn.duration(1500)} style={styles.nameText}>{contact.name}</Animated.Text><View style={styles.separator} /><Animated.Textentering={FadeIn.duration(1500)}style={styles.emailText}>{contact.email}</Animated.Text></View></Animated.View>);};const styles = StyleSheet.create({container: {width: 320,height: 90,flexDirection: 'row',alignItems: 'center',paddingHorizontal: 20,borderWidth: 1,borderColor: '#ffffff37',backgroundColor: '#ffffff04',borderRadius: 12,borderCurve: 'continuous',},content: { marginLeft: 15 },circleContainer: {backgroundColor: '#005CB7',justifyContent: 'center',alignItems: 'center',height: 50,width: 50,borderRadius: 25,},avatarText: { fontSize: 25, color: 'white' },nameText: { fontSize: 18, color: '#ffffff' },emailText: { fontSize: 15, color: '#d4d4d4' },separator: { height: 5 },});
<InteractiveContactItemcontact={{name: 'John Doe',email: 'john.doe@example.com',}}/>
npx expo install moti
npx expo install react-native-reanimated expo-linear-gradient
// These are just some nice defaultsconst SkeletonCommonProps = {colorMode: 'dark',transition: {type: 'timing',duration: 1000,},} as const;const ContactListSkeleton = () => {return (<View style={styles.container}><Skeleton.Group show={true}><Skeletonradius={'round'}height={50}width={50}{...SkeletonCommonProps}/><View style={styles.content}><Skeleton height={20} width={180} {...SkeletonCommonProps} /><View style={styles.spacer} /><Skeleton height={15} width={160} {...SkeletonCommonProps} /></View></Skeleton.Group></View>);};
type ContactListItemProps = {contact?: ContactInfo | null;};const ContactListItem: React.FC<ContactListItemProps> = ({ contact }) => {const isLoading = contact == null;if (isLoading) {return <ContactListSkeleton />;}return (<Animated.View style={styles.container} entering={FadeIn.duration(1000)}><View style={styles.circleContainer}><Text style={styles.avatarText}>{contact.name?.[0]}</Text></View><View style={styles.content}><Animated.Text entering={FadeIn.duration(1500)} style={styles.nameText}>{contact.name}</Animated.Text><View style={styles.separator} /><Animated.Textentering={FadeIn.duration(1500)}style={styles.emailText}>{contact.email}</Animated.Text></View></Animated.View>);};
const [contact, setContact] = useState<ContactInfo | null>(null);const fetchContact = async () => {setContact(null); // Show skeleton// Simulate API callawait new Promise((resolve) => setTimeout(resolve, 2000));setContact({name: 'John Doe',email: 'john.doe@example.com',});};
Every week I send out a newsletter sharing new things about React Native animations.