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 'moti/skeleton'
const Loader = ({ children }) => <Skeleton>{children}</Skeleton>
export default Loader
Example
Install
Expo Users
You'll also want to install expo-linear-gradient
.
Please note that you must have Reanimated 3 installed. See installation steps for more info.
expo install expo-linear-gradient
Non-Expo users
If you aren't using Expo, you can either use the expo-linear-gradient
package, or react-native-linear-gradient
.
Option 1: expo-linear-gradient
- npm
- Yarn
npm install expo-linear-gradient
yarn add expo-linear-gradient
Keep in mind you'll need to also install expo-modules-core
. Please make sure you complete any installation steps required for Expo's linear gradient component.
Option 2: react-native-linear-gradient
Click here to use react-native-linear-gradient
First, install the module resolver plugin:
npm install babel-plugin-module-resolver
Next, add this to your app's babel.config.js
's plugins
array.
// babel.config.js
module.exports = function (api) {
api.cache(true)
return {
plugins: [
[
'module-resolver',
{
root: ['./'],
alias: {
'moti/skeleton': 'moti/skeleton/react-native-linear-gradient',
},
},
],
],
}
}
This assumes you've installed react-native-linear-gradient
and followed any of its installation requirements.
Your skeletons will now use react-native-linear-gradient
. You can continue importing from moti/skeleton
.
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 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 'moti/skeleton'
const Spacer = ({ height = 16 }) => <MotiView style={{ height }} />
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 colorMode={colorMode} radius="round" height={75} width={75} />
<Spacer />
<Skeleton colorMode={colorMode} width={250} />
<Spacer height={8} />
<Skeleton colorMode={colorMode} width={'100%'} />
<Spacer height={8} />
<Skeleton colorMode={colorMode} width={'100%'} />
</MotiView>
</Pressable>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
padded: {
padding: 16,
},
})
<Skeleton.Group />
If you have many skeleton components, you can now wrap them with a single <Skeleton.Group show={loading} />
component. This will help you achieve this type of effect.
import { Image, Text } from 'react-native'
import { Skeleton } from 'moti/skeleton'
export function ListItem({ loading, item }) {
return (
<Skeleton.Group show={loading}>
<Skeleton>
<Image src={{ uri: image.avatar }} />
</Skeleton>
<Skeleton>
<Text>{item.title || ' '}</Text>
</Skeleton>
</Skeleton.Group>
)
}