Notes

Animating SVG in React Native

Edit on GitHub

React Native & Expo
3 minutes

So this started when i wanted to create placeholder components using SVG.. Here’s the complete code:

  1// Placeholder.tsx
  2import React, { useEffect } from 'react'
  3import { Animated, View } from 'react-native'
  4import Svg, {
  5  SvgProps,
  6  Circle,
  7  CircleProps,
  8  Rect,
  9  RectProps,
 10  CommonPathProps
 11} from 'react-native-svg'
 12
 13type Props = Readonly<{
 14  loading?: boolean
 15  shape?: 'circle' | 'rect'
 16  size: number
 17  height: number
 18  width: number
 19}>
 20
 21// set a value to be animated
 22const currentColor = new Animated.Value(0)
 23
 24// interpolate color value
 25const changeColor = currentColor.interpolate({
 26  inputRange: [0, 1, 2], // the values that the animation will transition from
 27  outputRange: ['gainsboro', 'whitesmoke', 'gainsboro'] // values that are animating
 28})
 29
 30// define the looping animation
 31const animateColor = ()=> {
 32    Animated.loop(
 33      Animated.sequence([
 34        Animated.timing(currentColor, {
 35          toValue: 2, // the value in interpolated output range that you want to go to
 36          duration: 2000 // ms
 37        }),
 38      ])
 39    ).start()
 40  }
 41
 42export default function Placeholder({
 43  loading = true,
 44  size = 50,
 45  height = 50,
 46  width = 50,
 47  radius = 8,
 48  shape = 'rect',
 49  fill = 'gainsboro',
 50  ...rest
 51}: Props &
 52  SvgProps &
 53  CircleProps &
 54  RectProps &
 55  CommonPathProps &
 56  any) {
 57
 58  useEffect(() => {
 59    // start the animation to change background color
 60    animateColor()
 61  })
 62
 63  const AnimatedSvg = Animated.createAnimatedComponent(Svg)
 64
 65  return (
 66    <View
 67    loading={loading}
 68    style={{margin: 8}}
 69    {...rest}
 70    >
 71      {shape === 'circle' && (
 72      <AnimatedSvg
 73        size={size}
 74        height={size}
 75        width={size}
 76        viewBox={`0 0 ${size * 2} ${size * 2}`}
 77        fill={loading ? changeColor : fill}
 78      >
 79        <Circle
 80          cx={size}
 81          cy={size}
 82          r={size}
 83        />
 84      </AnimatedSvg>
 85      )}
 86
 87      {shape === 'rect' && (
 88      <AnimatedSvg
 89        height={height}
 90        width={width}
 91        viewBox={`0 0 ${width} ${height}`}
 92        fill={loading ? changeColor : fill}
 93      > 
 94        <Rect
 95          x="0"
 96          y="0"
 97          rx={radius}
 98          width={width}
 99          height={height}
100        />
101      </AnimatedSvg>
102      )}
103    </View>
104  )
105}

Referencing the component:

 1// App.tsx
 2import React from 'react';
 3import { StyleSheet, View } from 'react-native';
 4import Placeholder from './Placeholder'
 5
 6export default function App() {
 7
 8  return (
 9    <View style={styles.container}>
10      <Placeholder />
11      <Placeholder loading={false}/>
12      <Placeholder shape='circle' size={150}/>
13      <Placeholder shape='rect' width={200} />
14      <Placeholder loading={false} shape='circle' size={100}/>
15    </View>
16  );
17}
18
19const styles = StyleSheet.create({
20  container: {
21    flex: 1,
22    alignItems: 'center',
23    justifyContent: 'center',
24  },
25});

Notes

  • SVG is not supported in react Native, using react-native-svg for that
  • Since SVG is not supported, it is also not supported by React Native Animated. So made the SVG a custom Animated component using createAnimatedComponent()
  • For example, to animate an SVG path, you’d make it an Animated component like so:
1import { Path } from 'react-native-svg'
2const AnimatedPath = Animated.createAnimatedComponent(Path)
  • The prop types are there because i’m using Typescript

  • The <Placeholder> component can take

    • a shape prop, the two options are circle or rect
    • a size for circle
    • height and width for rect
    • loading: boolean to decide whether to animate the background color or not
    • Default values have been added as fallbacks (rx, height, width, loading, size, shape and fill)
  • I originally had height and width for circle as well, with size as a complimentary value. But i ended up getting rid of those since it is a circle.. Common usage will always have the same height and width