Skeleton

Moti's skeleton component is great for showing animated loading states. It works on all platforms, including web.

import React from 'react'
import { Skeleton } from '@motify/skeleton'
const Loader = ({ children }) => <Skeleton>{children}</Skeleton>
export default Loader

Install#

The skeleton ships as its own package, since it has dependencies outside of moti:

npm install @motify/skeleton

You'll also want to install expo-linear-gradient.

Please note that you must have Reanimated 2 installed. See installation steps for more info.

If you're using Expo:#

expo install expo-linear-gradient

If you aren't using Expo:#

npm install expo-linear-gradient

Please make sure you complete any installation steps required for Expo's linear gradient component.

Video#

Usage#

Show/hide#

Skeleton will hide when data exists by default.

<Skeleton>{!!data ? <Data /> : null}</Skeleton>

You can always show the skeleton:

<Skeleton show={loading}>
<Data />
</Skeleton>

Or hide it:

<Skeleton show={false}>{!!data ? <Data /> : null}</Skeleton>

Border radius#

Use radius to show a circle, square, or custom border radius. Defaults to 8.

Circle#

<Skeleton height={48} width={48} radius="round">
{!!data ? <Data /> : null}
</Skeleton>

Square#

<Skeleton height={48} width={48} radius="square">
{!!data ? <Data /> : null}
</Skeleton>

Custom radius#

<Skeleton radius={16}>{!!data ? <Data /> : null}</Skeleton>

Color modes#

light or dark

<Skeleton colorMode="light" />

Custom colors#

<Skeleton colors={['blue', 'cyan']} />

Custom animation delay#

<Skeleton delay={250} />

Custom animation transition#

<Skeleton
transition={{
translateX: {
// defaults to a 3000ms timing function
type: 'spring',
},
}}
/>

Full example#

Here's the code from the video above:

import React, { useReducer } from 'react'
import { StyleSheet, Pressable } from 'react-native'
import { MotiView } from 'moti'
import { Skeleton } from '@motify/skeleton'
const Spacer = ({ height = 16 }) => <MotiView style={{ height }} />
const transition = {
opacity: {
duration: 300,
},
}
export default function HelloWorld() {
const [dark, toggle] = useReducer((s) => !s, true)
const colorMode = dark ? 'dark' : 'light'
return (
<Pressable onPress={toggle} style={styles.container}>
<MotiView
transition={{
type: 'timing',
}}
style={[styles.container, styles.padded]}
animate={{ backgroundColor: dark ? '#000000' : '#ffffff' }}
>
<Skeleton
transition={transition}
colorMode={colorMode}
radius="round"
height={75}
width={75}
/>
<Spacer />
<Skeleton transition={transition} colorMode={colorMode} width={250} />
<Spacer height={8} />
<Skeleton
transition={transition}
colorMode={colorMode}
width={'100%'}
/>
<Spacer height={8} />
<Skeleton
transition={transition}
colorMode={colorMode}
width={'100%'}
/>
</MotiView>
</Pressable>
)
}
const styles = StyleSheet.create({
shape: {
justifyContent: 'center',
height: 250,
width: 250,
borderRadius: 25,
marginRight: 10,
backgroundColor: 'white',
},
container: {
flex: 1,
justifyContent: 'center',
},
padded: {
padding: 16,
},
})