Blog
Deep links

Make your custom charts with react-native-svg-charts and typescript. Step-by-step tutorial

React-native-charts-svg - you need nothing more to display data in your application. This article will help you understand how to create different types of charts, and personalize them. Tutorial step by step!
Tags
Typescript
React Native
By
Conrad Gauza
11 Jan 2022
5 minute read
Share this post

Line steps shape

Let’s change the shape of our line for the steps. A full list of shapes can be found HERE .

import { LineChart, Grid } from 'react-native-svg-charts'
import { Shadow, Gradient } from './chartAdds'

export const LineChartComponent = () => {
    const data2 = [80, 10, 95, 48, 24, 67, 51, 12, 33, 0, 24, 20, 50]

    return (
        <LineChart
            style={{ height: 200 }}
            gridMin={-20}
            gridMax={120}
            data={data2}
            curve={shape.curveStep}
            svg={{
                strokeWidth: 2,
                stroke: 'url(#gradient)' }}
            contentInset={{ top: 20, bottom: 20 }}>
            <Shadow />
            <Grid />
            <Gradient />
        </LineChart>
    )
}
lineChart steps

Partial line with shadow

Now it’s time to provide a new component - Clips. It will let us make a partial line with the shadow.
What do I mean exactly? Let’s take a look at the chart below:

lineChart partial

As we can see, the line goes only to the 5th point. After that, we can see only the shadow.

  • Clips - this component creates the area where the line of the chart is visible. Clips limits the elements inside to their own dimensions.

That’s how we use it inside the chart component:

import { LineChart, Grid } from 'react-native-svg-charts'
import { Shadow, Gradient, Clips } from './chartAdds'

export const LineChartComponent = () => {
    const data2 = [80, 10, 95, 48, 24, 67, 51, 12, 33, 0, 24, 20, 50]
    const indexToClipFrom = 4

    return (
        <LineChart
            style={{ height: 200 }}
            gridMin={-20}
            gridMax={120}
            data={data2}
            contentInset={{ top: 20, bottom: 20 }}
            svg={{
                stroke: 'url(#gradient)',
                strokeWidth: 2,
                clipPath: 'url(#clip-path-1)',
            }}>
            <Shadow />
            <Gradient />
            <Grid />
            <Clips indexToClipFrom={indexToClipFrom} />
        </LineChart>
    )
}

We need to pass prop indexToClipFrom to the Clips component. It’s the index number, that we would like to cut the line at. At the moment it’s set to 4, which means that our colored line, will be cut at point number 5. Also, we need to add clipPath property to the svg prop. It’s an ID of the clip we would like to use.

So what happens inside the Clips component?

interface ClipProps {
    x: (arg: number) => number
    indexToClipFrom: number
}

export const Clips = (props: Partial<ClipProps>) => {
    const { x, indexToClipFrom } = props as ClipProps
    return (
        <ClipPath id="clip-path-1">
            <Rect x={'0'} y={'0'} width={x(indexToClipFrom)} height={'100%'} />
        </ClipPath>
    )
}
  • ClipPath - a container for the svg element Rect. It also holds the ID which is a key to invoke this structure. We used it in the svg props clipPath property. It puts to use the old JS rule, according to which we can call an element using its ID.
  • Rect - svg basic element which creates a rectangle

If we would like to change the settings of the Clips component or to be exact: ClipPath with id="clip-path-1", we need to know which props are responsible for in Rect element:

  • x - position on the axis X (in px). It’s now set to 0, which means it sticks to the left border of the chart.
  • y - position on the axis Y (in px). It’s now set to 0, which means it sticks to the top border of the chart.
  • width - it’s exactly the width of the rectangle. In this case, it’s calculated based on the number of the index we pass. It can be just a number also, which will give us width in px.
  • height - literally the height of the rectangle. It’s now set at 100%, so the rectangle covers the entire height of the chart.

Why does it affect only the line? Because it’s injected inside the chart. Grid and Gradient are separated components. That’s the magic that goes on behind the scene :)

We can also add DashedLine component to cover the shadow. There is not much to explain here, it’s basically the line width strokeDasharray props. You can find a ready example in the repository.

Let’s move on to the multiple lines chart!

Multiple lines chart

Now we need to put few different data lines on one chart. To complicate things a bit, we will also add the decorators to all points at the lines. Like you’ve probably guessed, we need to work with our data array again. In fact, we need to create the combined array. Combined of what? The data arrays.

We can imagine we’ve got four number[]:

const data9 = [13, 4, 91, 67, 42, 27, 10, 20]
const data10 = [40, 83, 60, 30, 75, 90, 27, 52]
const data11 = [80, 60, 25, 48, 24, 67, 51, 12]
const data12 = [10, 20, 41, 4, 50, 33, 21, 47]

We need to create an object from each of them, and push them into our combined data array:

export const quadrupleLineChart = [
    {
        data: data9,
        svg: {
            stroke: 'red',
            strokeWidth: 1,
        },
    },
    {
        data: data10,
        svg: {
            stroke: 'blue',
            strokeWidth: 1,
        },
    },
    {
        data: data11,
        svg: {
            stroke: 'orange',
            strokeWidth: 1,
        },
    },
    {
        data: data12,
        svg: {
            stroke: 'green',
            strokeWidth: 1,
        },
    },
]

So what happened?
We took each data array and created an object which contains:

  • this particular array
  • svg props for each array

We could just push the data array, without svg, but then all the lines would have the same color, and it wouldn’t be legible at all. We push all that data to one array, probably to compare them, so we need to distinguish them at a glance. Our LineChart component is so intelligent, that when we pass this combined array to the data props, we don’t even need to pass yAccessor there. All we need to do is to pass the combined data to the LineChart data props (if we name the key of the object “data”). Our LineChart component looks easy as always:

import { LineChart, Grid } from 'react-native-svg-charts'
import { Shadow, Gradient, Clips } from './chartAdds'
import { quadrupleLineChart } from '@/data/data'
import { MultipleLinesChartDecorator } from '@/screens/LineChartComponent/chartAdds'

export const LineChartComponent = () => {
    return (
        <LineChart
            style={{ height: 200 }}
            gridMin={-20}
            gridMax={120}
            data={quadrupleLineChart}
            contentInset={{ top: 20, bottom: 20 }}>
            <Grid />
            <MultipleLinesChartDecorator combinedData={quadrupleLineChart} />
        </LineChart>
    )
}

With one exception…
What is MultipleLinesChartDecorator, and how does it work?
It’s a variation of well know Dots component. I’ve already explained it in the part Line, circle decorators and grid .
Let’s see:

interface DecoratorProps {
    x: any
    y: any
    combinedData: {
        data: number[]
        svg: {
            stroke: string
            strokeWidth: number
        }
    }[]
}

const min = 1
const max = 10000000

const uniqueKey = (index: number): number => {
    return Math.floor(Math.random() * (max - min + 1)) + min + index
}

export const MultipleLinesChartDecorator = (props: Partial<DecoratorProps>) => {
    const { x, y, combinedData } = props as DecoratorProps

    return (
        <>
            {combinedData &&
            combinedData.map((item, index) => {
                return (
                    <Svg key={uniqueKey(index)}>
                        {item.data.map((value, index) => (
                            <Circle
                                key={uniqueKey(index)}
                                cx={x(index)}
                                cy={y(value)}
                                r={2.5}
                                stroke={'rgb(0, 0, 0)'}
                                fill={'white'}
                            />
                        ))}
                    </Svg>
                )
            })}
        </>
    )
}

If you take a closer look at the MultipleLinesChartDecorator and Dots components - there is only one difference in the operation process. We just need multiple Dots components for each data array - for each object in a new combined array. In other words, we need to iterate over the combined data array. One more difference is that we have to pass data by ourselves. Our MultipleLinesChartDecorator is getting lost when it’s trying to pull data, with data keys inside data object etc.
We need to type the props for MultipleLinesChartDecorator, pass the combined data as a prop (x and y are still taken from the chart lexical scope), and use it instead of data pulled previously pulled from the chart’s lexical scope. Now we work on the data we passed ourselves. The effect is visible below:

lineChart multiple dots

If we need to display multiple data on one chart, that is a very good way to do it.

Line chart with average

Now we have all those great examples above, and I hope they will come in really useful for you. But what if we need to put an average value on the chart? Library react-native-charts-svg doesn’t offer us any built solution, but we can make one by ourselves just like we have done it many times before.

We need an extra line for the average value. If we want to display a line on the chart, we need a data array. One that is filled with the average value. Let’s prepare a function for that:

export const createAverageValuesArray = (data: number[]): number[] => {
    const averageValue = data.reduce((a, b) => a + b) / data.length
    return Array(data.length).fill(averageValue)
}

Our function takes the original data array, calculates the average, and creates an array with the averages value. The important part is that the length of the new array has to be the same as the original array length. Axis X operates on the index, so if the data array has 10 elements, and our average array would have 5 elements, then the data line will be too long for the full width, but the average line will take only half of that width. That’s not what we’re after. Let’s remember that the average line should have the same number of elements in the array as the original data array.

The next step is to create a combined data array as we’ve done in the previous example. We can also make a function for that:

export const createAverageValuesArray = (data: number[]): number[] => {
    const averageValue = data.reduce((a, b) => a + b) / data.length
    return Array(data.length).fill(averageValue)
}

We didn’t set a stroke (color) property in our objects. Only strokeWidth and strokeDasharray (only for average). In that case, the color for both lines will be taken from svg props passed to the LineChart.
Now we just need to import the LineChart component, and put it all together:

import { LineChart, Grid } from 'react-native-svg-charts'
import { Shadow, Gradient } from './chartAdds'

export const LineChartWithAverage: VFC<Props> = () => {
    const data2 = [80, 10, 95, 48, 24, 67, 51, 12, 33, 0, 24, 20, 50]

    const averageValuesArray = createAverageValuesArray(data2)
    const dataWithAverageValue = composeDataWithAverageValue(data2, averageValuesArray)

    return (
        <>
            <Container>
                <LineChart
                    style={{ height: 300 }}
                    gridMin={-50}
                    gridMax={150}
                    data={dataWithAverageValue}
                    curve={shape.curveNatural}
                    svg={{
                        stroke: 'url(#gradient)',
                    }}
                    contentInset={{ top: 20, bottom: 20 }}>
                    <Shadow />
                    <Gradient />
                </LineChart>
            </Container>
        </>
    )
}

The result is visible below:

lineChartWithAverage

The example with Multiple Line Chart with averages is available in the repository.

Line chart props summary

Let’s summarize the props that we already used in Line charts:

  • data - array of arbitrary data. For example, it can be array of numbers or objects.
  • style - a well-known line-style prop. In this case it’s a style for the chart CONTAINER – not the chart itself. Height is set at a default 0, so we need to pass the expected height by ourselves. Always. Here, we can treat the container not like a box, but more like a canvas to write on. If we do not set the height, there will be nothing to put the chart on.
  • svg - this prop is a style-prop for the chart itself. It can take several properties. A full list of them is available HERE
  • gridMin - minimal visible chart value. By default, it’s set at the minimal value of the data. We can set the value by ourselves too, but if we set it at a higher value than the minimal data value, the system will take the minimal data value. For example if the min value in data array is -20 and we set it to 0, then the system will set it at -20. But if we set it to -40, the system will set it at -40. In other words, the system always compares our value with the lowest value in the array, and sets the lower one.
  • gridMax - maximal visible chart value. It works exactly the same as gridMin, but in the opposite manner. The system always compares the value provided by us with the highest value from the data array, and sets the higher one.
  • contentInset - object with four values: top, bottom, left and right. It sets the distance between the component border and chart border. For example if we set { botom: 30 }, then the distance between the component’s bottom border and the chart’s bottom border will be 30px. We can consider it as a padding for chart.
  • curve - the shape of our area chart. The d3 library has been used here to allow the chart to take many different shapes. All the possibilities are listed HERE . You can also read more about the d3 library there. To pull some shape from the library, we need to import the main object from it and rename it. shape will be a good choice. Now just pull the chosen shape from the shape object. For example curve={shape.curveNatural}.

What else we can use:

  • yAccessor - we pass it a function which tell us, which key in the objects array should be taken as a value to display on the chart. In other words we pass the object key the chart should iterate at. Another thing is that thanks to it, chart iterating over the objects array, can take other properties of the object (bar) and inject it to a specific bar.
  • xAccessor - thanks to that function, we can provide a more complicated data structure and choose the value we want to use from the keys in objects array (used for axis X) - we will use this props later for other charts, so you will become familiar with it
  • yScale - A function that determines the scale of axis Y. As said in the documentation it’s not very well tested
  • xScale - A function that determines the scale of axis X. As said in the documentation it’s not very well tested
  • xMin - the minimal visible value of axis X. Not really useful if you are looking to use all the available space to show data. Let’s imagine your data starts at 0. If you set it to -10, you will get an empty white space on the left side. If you set it at 10, then part of your data will be invisible. Mobile devices are usually narrow. I recommend using the available space to the maximum to increase readability
  • xMax - maximal visible value of axis X. Same situation as with xMin
  • yMin - minimal visible value of axis Y. We can get the same effect using gridMin
  • yMax - maximal visible value of axis Y. We can get the same effect using gridMan
  • numberOfTicks - the number of lines visible on the background grid (of course only when we use an additional component Grid)

(Remember that some props are available only in a specific types of charts. For example, you cannot use start in the LineChart)

Pie Chart

The pie chart is very useful for demonstrating percentage participation. We will create a few examples to get through props and give you something nice and ready to use.

Pie chart data

To display the most basic example of react-native-svg-charts PieChart, we just need a data array, width, and height. Here we cannot pass basic number[], as we’ve done it before’. Every object in the array of objects needs to have a specific structure. Let’s take a look at the PieChartData interface:

export interface PieChartData {
    svg?: Partial<PathProps> | undefined;
    key: string | number;
    value?: number | undefined;
    arc?: {
        outerRadius?: number | string | undefined;
        cornerRadius?: number | string | undefined;
    } | undefined;
}
  • svg - a well known style props for svg elements. We can check the full list of possible properties HERE
  • key - ID of each slice of the pie
  • value - the value of each slice of the pie
  • arc - object with values:
  • outerRadius - radius of the single slice of the pie. It can be set separately, different from than whole pie radius.
  • cornerRadius - sets the corner rounding angle of each slice of the pie

Even if the PieChartData interface tells us, that only key property is required, it is not entirely true. If we do not pass svg with fill or stroke property set, then our piece will not be visible. If we do not pass value, then it will be set to undefined by default, which means not visible - again. We can assume that if we want for each piece to be visible it has to get at least:

  • svg - fill or stroke
  • key - required
  • value

With that knowledge, we can create our most basic PieChart.

Basic Pie Chart

First let’s prepare the function which will take an array of numbers as an argument and return an array of data, required for the PieChart:

export const getPieChartData = (data: number[]) => {
    return data.map((item, index) => {
        const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16)

        return {
            key: index,
            value: item,
            svg: { fill: randomColor },
        }
    })
}

Now we can create our component:

import { PieChart} from 'react-native-svg-charts'
import { getPieChartData } from './utilityFunctions'

export const PieChartComponent = () => {
    const data10 = [40, 83, 60, 30, 75, 90, 27, 52]
    const pieChartData = getPieChartData(data10)

    return (
        <PieChart
            style={{ width: 200, height: 200 }}
            data={pieChartData}
        />
    )
}

The result of our work is visible below:

pieChart base

Looks pretty nice, doesn’t it? But of course, we will not stop at the base form.

Inner radius, pad angle and sort

We will now focus on the three new props we can use in the PieChart:

  • innerRadius - it’s a radius of the inner circle (in px). The bigger it is, the slimmer is our ‘donut’.
  • padAngle - this property is not working very well. To be honest it’s hard to control it. It should be the angle between slices. But it has an important function: if you set it for ‘0’, the distance between the slices will disappear. So we should remember two options. Default, when we have our distance between the slices or 0 when that distance is gone.
  • sort - sort the slices. If you take a closer look at the base example above, you notice that the slices are arranged from the smallest to the largest one. That’s the default behavior, but we can change it using this prop.

Once again, we will reach for getPieChartData function and props listed above to create another example:

import { PieChart} from 'react-native-svg-charts'
import { getPieChartData } from './utilityFunctions'

export const PieChartComponent = () => {
    const data10 = [40, 83, 60, 30, 75, 90, 27, 52]
    const pieChartData = getPieChartData(data10)

    return (
        <PieChart
            style={{ width: 200, height: 200 }}
            innerRadius={0}
            data={pieChartData}
            padAngle={0}
            sort={(a, b) => b.key - a.key}
        />
    )
}

We set innerRadius and padAngle(angle between slices) to 0. Slices now are sorted by the key, and not by the value like before:

pieChart innerRadius padAngle sort

The result is not a donut, but literally a pie :)

Corner radius and labels

Now we will consider a more complicated case.
First let’s create a donut, where each slice has rounded angles.
We already know that each of our slices can get arc props, which is an object with cornerRadius property inside.

  • cornerRadius - sets the corner rounding angle of each slice of the pie (set in px)

We can modify our getPieChartData function by adding the cornerRadius property:

export const getPieChartDataRounded = (data: number[]) => {
    return data.map((item, index) => {
        const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16)

        return {
            key: index,
            value: item,
            svg: { fill: randomColor },
            arc: { cornerRadius: 5 },
        }
    })
}

Other props that will be necessary for this example are:

  • outerRadius - sets the distance between the center of the pie and its outer border (set in px or %. If set in % - ‘100% means that it takes all the space to the closest container border )
  • labelRadius - sets the distance between the pie center and eventual labels, we want to add (set in px or %)

Now we’re ready to create PieChart with rounded angles of the slices:

import { PieChart} from 'react-native-svg-charts'
import { getPieChartDataRounded } from './utilityFunctions'

export const PieChartComponent = () => {
    const data10 = [40, 83, 60, 30, 75, 90, 27, 52]
    const pieChartDataRounded = getPieChartDataRounded(data10)

    return (
        <PieChart
            style={{ width: 300, height: 300 }}
            data={pieChartDataRounded}
            innerRadius={35}
            outerRadius={70}
            labelRadius={120}
            sort={(a, b) => b.key - a.key}
        />
    )
}
pieChart roundedCorners

Another step is to create Labels component:

import { Circle, Line, G, Text } from 'react-native-svg'
import { PieChartData } from 'react-native-svg-charts'

interface LabelsProps {
    slices: {
        pieCentroid: string
        labelCentroid: string
        data: PieChartData
    }[]
}

export const Labels = (props: Partial<LabelsProps>) => {
    const { slices } = props as LabelsProps
    return (
        <>
            {slices.map((slice, index) => {
                const { labelCentroid, pieCentroid, data } = slice
                return (
                    <G key={index}>
                        <Line
                            x1={labelCentroid[0]}
                            y1={labelCentroid[1]}
                            x2={pieCentroid[0]}
                            y2={pieCentroid[1]}
                            stroke={data?.svg && data.svg.fill}
                        />

                        <G x={labelCentroid[0]} y={labelCentroid[1]}>
                            <Circle r={22} fill={data?.svg && data.svg.fill} />
                            <Circle r={17} fill={'white'} />
                            <Text
                                key={index}
                                x={-0.5}
                                y={1.5}
                                fill={'black'}
                                textAnchor={'middle'}
                                alignmentBaseline={'middle'}
                                fontSize={16}
                                fontWeight={'bolder'}
                                stroke={'white'}
                                opacity={'1'}
                                strokeWidth={0.4}>
                                {data.value}
                            </Text>
                        </G>
                    </G>
                )
            })}
        </>
    )
}

What is going on there, and what is it made of?
Let’s start from the beginning.
PieChart prepare for us slices[], which can be used to set labels for the chart. slices[] is composed of objects that contain:

  • labelCentroid - the array with 2 coordinates: [x, y] that determines the center of the label
  • pieCentroid - the array with 2 coordinates: [x, y] that determines the center of the pie slice
  • data - object with information about the slice

We already know that G component is a wrapper for SVG elements. Line is an SVG element for the line, with x1, x2, y1, y2 coordinates, and stroke as o color of the line taken from the data object (that we previously passed as a prop to the PieChart). The first Circle is an outer circle of the label, and the second Circle is the inner circle of the label. The last element Text is the value, taken again from the data object. Like in every element above, x, y are the coordinates and r is the circle radius. Text element can take various props, depending on what style we would like to apply for it(please check Text props in the react-native-svg types).

Now let’s apply our Label component to the PieChart that we made before:

import { PieChart } from 'react-native-svg-charts'
import { Labels } from '@/screens/PieChartComponent/chartAdds'
import { getPieChartDataRounded } from './utilityFunctions'

export const PieChartComponent = () => {
    const data10 = [40, 83, 60, 30, 75, 90, 27, 52]
    const pieChartDataRounded = getPieChartDataRounded(data10)

    return (
        <PieChart
            style={{ width: 300, height: 300 }}
            data={pieChartDataRounded}
            innerRadius={35}
            outerRadius={70}
            labelRadius={120}
            sort={(a, b) => b.key - a.key}>
            <Labels />
        </PieChart>
    )
}

I hope you agree that the final effect looks interesting ;) :

pieChart labels

Slice outer radius

We already know what the outerRadius property does. Just to remind you, it sets the distance between the circle center and its border. So what if we set it for a slice in our data array? The slice will become smaller, or bigger than the circle itself.

Let’s create our data first:

export const data13 = [
    {
        key: 1,
        value: 50,
        svg: { fill: '#600080' },
        arc: { outerRadius: '120%', cornerRadius: 10 },
    },
    {
        key: 2,
        value: 50,
        svg: { fill: '#9900cc' },
        arc: { cornerRadius: 5 },
    },
    {
        key: 3,
        value: 40,
        svg: { fill: '#c61aff' },
        arc: { cornerRadius: 5 },
    },
    {
        key: 4,
        value: 95,
        svg: { fill: '#d966ff' },
        arc: { cornerRadius: 5 },
    },
    {
        key: 5,
        value: 35,
        svg: { fill: '#ecb3ff', stroke: 'purple', strokeWidth: 2 },
        arc: { outerRadius: '120%', cornerRadius: 10 },
    },
]

We set the outerRadius of two slices to 120%. It’s useful if we want to highlight particular data. The effect is visible below:

pieChart sliceSouterRadius
import { PieChart} from 'react-native-svg-charts'
import { getPieChartDataRounded } from './utilityFunctions'
import { data13 } from '@/data/data'

export const PieChartComponent = () => {
    return (
        <PieChart
            style={{ width: 300, height: 300 }}
            outerRadius={'70%'}
            innerRadius={40}
            data={data13}
        />
    )
}

We can also create a function which will make each slice in the pie smaller than the previous one:

export const getPieChartDataSteps = () => {
    const keys = ['google', 'facebook', 'linkedin', 'youtube', 'Twitter']
    const values = [15, 25, 35, 45, 55]
    const colors = ['#600080', '#9900cc', '#c61aff', '#d966ff', '#ecb3ff']
    return values.map((key, index) => {
        return {
            key,
            value: values[index],
            svg: { fill: colors[index], stroke: colors[index - 1], strokeWidth: 2 },
            arc: { outerRadius: 70 + values[index] + '%', padAngle: 0.1 },
        }
    })
}

Thanks to that, we can prepare examples like this one:

pieChart slicesOuterRadius2
import { PieChart} from 'react-native-svg-charts'
import { getPieChartDataRounded } from './utilityFunctions'
import { data13 } from '@/data/data'

export const PieChartComponent = () => {
    const pieChartDataSteps = getPieChartDataSteps()

    return (
        <PieChart
            style={{ width: 310, height: 300 }}
            outerRadius={'80%'}
            innerRadius={'45%'}
            data={pieChartDataSteps}
        />
    )
}

Nested pie charts

We can also make more complicated charts, with a very simple method. We can nest different pie charts in other ones, by manipulating their innerRadius and outherRadius:

Let’s use the getPieChartData function again:

export const DeepPieChart = () => {
    const pieChartData = getPieChartData(data10)
    const pieChartData2 = getPieChartData(data11)
    const pieChartData3 = getPieChartData(data12)
    const pieChartData4 = getPieChartData(data9)
    const pieChartData5 = getPieChartData(data8)
    const pieChartData6 = getPieChartData(data5)
    const pieChartData7 = getPieChartData(data2)

    return (
        <>
            <PieChart
                style={{ width: 250, height: 250 }}
                innerRadius={25}
                outerRadius={35}
                data={pieChartData}>
                <Svg>
                    <PieChart
                        style={{ width: 250, height: 250 }}
                        innerRadius={40}
                        outerRadius={50}
                        data={pieChartData2}>
                        <Svg>
                            <PieChart
                                style={{ width: 250, height: 250 }}
                                innerRadius={55}
                                outerRadius={65}
                                data={pieChartData3}>
                                <Svg>
                                    <PieChart
                                        style={{ width: 250, height: 250 }}
                                        innerRadius={70}
                                        outerRadius={80}
                                        data={pieChartData4}>
                                        <Svg>
                                            <PieChart
                                                style={{ width: 250, height: 250 }}
                                                innerRadius={85}
                                                outerRadius={95}
                                                data={pieChartData5}>
                                                <Svg>
                                                    <PieChart
                                                        style={{ width: 250, height: 250 }}
                                                        innerRadius={100}
                                                        outerRadius={110}
                                                        data={pieChartData6}>
                                                        <Svg>
                                                            <PieChart
                                                                style={{ width: 250, height: 250 }}
                                                                innerRadius={115}
                                                                outerRadius={125}
                                                                data={pieChartData7}
                                                            />
                                                        </Svg>
                                                    </PieChart>
                                                </Svg>
                                            </PieChart>
                                        </Svg>
                                    </PieChart>
                                </Svg>
                            </PieChart>
                        </Svg>
                    </PieChart>
                </Svg>
            </PieChart>
        </>
    )
}

Like I said before, we manipulate circles innerRadius and outerRadius. Every subsequent pieChart’s innerRadius is 15px wider. The same goes for outerRadius. Another important thing is that we need to wrap each nested pieChartin Svg component. In other cases, data from the higher-order pieChart will overwrite that of the nested pieChart. We would get 7 exactly the same pieCharts. But we’re already aware of that, so we didn’t make this mistake. We got just the result that we expected:

nestedPieCharts

Pie chart props summary

Let’s summarize the props that we already used in the Pie charts

  • data - an array of arbitrary data. For example, it can be an array of numbers or objects.
  • style - a well known line-style prop. In this case, it’s a style for the chart’s CONTAINER - not the chart itself. Height is set at a default 0, so we need to pass the expected height by ourselves. Always. Here, we can treat the container not as a box, but more like a canvas to write on. If we do not set the height, there will be nothing to put the chart on.
  • svg - this prop is a style-prop for the chart itself. It can take several properties. A full list of them is available HERE
  • outerRadius - sets the distance between the center of the pie and its outer border (set in px or %. If set in % - ‘100% means that it takes all the space to the closest container border )
  • labelRadius - sets the distance between the pie center and eventual labels, we want to add (set in px or %)
  • innerRadius - it’s a radius of the inner circle (in px). The bigger it is, the slimmer is our ‘donut’.
  • padAngle - this property is not working very well. To be honest, it’s hard to control it. It should be the angle between the slices. That said, it has an important function: if you set it to ‘0’, the distance between the slices will disappear. So we should remember two options. Default, when we have our distance between the slices or 0 when that distance is gone.
  • sort - sort the slices. The slices are arranged from the smallest to the largest one (value). That’s the default behavior, but we can change it using this prop.

What else we can use:

  • startAngle - the start angle in the radians of the entire pie
  • endAngle - the end angle in the radians of the entire pie
  • valueAccessor - we pass it a function that tells us, which key in the object’s array should be taken as a value to display on the chart. In other words, we pass the object key the chart should iterate at. Another thing is that thanks to it, chart iterating over the object’s array, can take other properties of the object (slice) and inject them into a specific slice.

Progress Circle

One of the simplest charts. Basically, we can consider it as a line in the shape of a circle. But, as we have already seen working with LineChart, the simplest solutions can be most useful. And it’s a fact that the progress circle is used very often.

Basic Progress circle

There is not much to say about the basic progress circle, so let’s create one and see what we need to use, to display it:

import { ProgressCircle } from 'react-native-svg-charts'

export const ProgressCircleComponent = () => {
    return (
        <ProgressCircle
            style={{ height: 200 }}
            progress={0.6}
            progressColor={'rgb(192, 0, 0)'}
        />
    )
}

The first difference that we should notice is that we have no data array. The reason is simple - the progress circle displays no data. It displays… progress :)
So instead of data props, we have progress props. Also, instead of svg or color props, we have the progressColor props.

To display the basic progress bar we need three props then:

  • style - like always we need to set height, to put our svg elements on something. Of course, we can set a lot of other properties. All of them are listed HERE.
  • progress - progress we want to display. We set it in the range 0-1, which corresponds to values from 0% to 100%.
  • progressColor - color of progressBar. Not the background but the progress line itself.

Now it looks like this:

basicProgressBar

Quite simple, but often it’s enough for us.
Anyway, let’s have a little fun with that.

Start angle and end angle

Like you’ve probably guessed, we can change the place, where the progress bar starts and ends on the circle. We have two props responsible for that.

  • startAngle - a point on the circle, where the progress bar starts (number of degrees). Default setting: Math.PI
  • endAngle - a point on the circle, where the progress bar ends (number of degrees). Default setting: -Math.PI

So let’s reverse our progress bar:

import { ProgressCircle } from 'react-native-svg-charts'

export const ProgressCircleComponent = () => {
    return (
        <ProgressCircle
            style={{ height: 200 }}
            progress={0.6}
            progressColor={'rgb(134, 65, 244)'}
            startAngle={-Math.PI}
            endAngle={Math.PI}
        />
    )
}

Now our progress bar looks like this (we’ve also changed the progress bar color):

progressBar reversed

As we know, we use Math.PI to calculate the circle circumference. Nothing prevents us from reducing it a bit:

import { ProgressCircle } from 'react-native-svg-charts'

export const ProgressCircleComponent = () => {
    return (
        <ProgressCircle
            style={{ height: 200 }}
            progress={0.7}
            progressColor={'rgb(249, 166, 2)'}
            startAngle={-Math.PI * 0.8}
            endAngle={Math.PI * 0.8}
        />
    )
}

We reduced the circle’s circumference by 20% from each side. Let’s see what effect we have achieved:

progressBar partial

Progress number corner radius and stroke width

Now we can make something still easy, but more complicated compared to the previous examples:

import { ProgressCircle } from 'react-native-svg-charts'

export const ProgressCircleComponent = () => {
    return (
        <ProgressCircle
            style={{ height: 200 }}
            progress={0.8}
            progressColor={'rgb(249, 166, 2)'}
            startAngle={-Math.PI}
            cornerRadius={0}
            strokeWidth={15}
            endAngle={Math.PI}>
          <Gradient />
          <Text
                  x={-0.5}
                  y={1.5}
                  fill={'black'}
                  textAnchor={'middle'}
                  alignmentBaseline={'middle'}
                  fontSize={36}
                  fontWeight={'bolder'}
                  stroke={'white'}
                  opacity={'1'}
                  strokeWidth={0.4}>
            80%
          </Text>
    )
}

We’ve inserted the well-known Gradient component and styled the Text component. It’s nothing new for us. But we have two new props that we’ve already use above:

  • cornerRadius - this prop is responsible for the angle of our line
  • strokeWidth - it’s literally the width of our progress line

When you will take a closer look at the end of our progress line, you will notice that it’s rounded. That’s because the default value of cornerRadius is 45 (45 degrees). Now we set it to 0, so it will be flat on the end. Also, we used strokeWidth and set it to 15, which makes it 3 times wider (default is 5).

The effect is visible below:

progressCircle wide

Combine stroke width with the angles and text - Speedometer

Ok, let’s create a simple speedometer in this case, because… why not? :) Our maximum speed will be 400 km/h. We will set our endAngle for Math.PI / 2 and startAngle for the -Math.PI / 2. In other words, out circle will take 50% of the original size. Also we will set stroke with for 7px, so it will be little wider. Last thing we will set will be backgroundColor. We have to remeber that if we want to change background color of our COMPONENT, then we will do that in the style props. Here we set background color, for the circle line, we operate at:

-backgroundColor - the color of the circle below the our line (circle in the background, under progress progress line)

Firstly we have to remember, that our progress have to be passed as a number in range 0 - 1. So, we can create a simple function which will convert our speed to the desired value:

const calculateSpeedForProgress = (speed: number, maxSpeed: number) => {
    return speed / maxSpeed
  }

We can provide any speed and max speed values, and we will always get the right value for the progress circle.

Ok, let’s create our Speedometer component then:

import { ProgressCircle } from 'react-native-svg-charts'

export const Speedometer = () => {

  const maxSpeed = 400
  const speed = 234

  const calculateSpeedForProgress = (speed: number, maxSpeed: number) => {
    return speed / maxSpeed
  }

  const calculatedProgress = calculateSpeedForProgress(speed, maxSpeed)

  return (
          <ProgressCircle
                  style={{ height: 200 }}
                  progress={calculatedProgress}
                  progressColor={'rgb(249, 166, 2)'}
                  backgroundColor={'rgba(194, 65, 244, 0.1)'}
                  strokeWidth={7}
                  startAngle={-Math.PI / 2}
                  endAngle={Math.PI / 2}>
              <Gradient />
              <Text
                 x={1}
                 y={-12}
                 fill={'black'}
                 textAnchor={'middle'}
                 alignmentBaseline={'middle'}
                 fontSize={27}
                 fontWeight={'bolder'}
                 stroke={'white'}
                 opacity={'1'}
                 strokeWidth={0.4}>
                {`${speed} km/h`}
              </Text>
            </ProgressCircle>
          )
}

Here is our basic Speedometer:

speedometer

Nested multiple progress circles

We can make quite the same trick, as we didi with pieChart. We can nest progress circles inside another ones. Here the trick is a little more complicated because, we have to set justifyContent property in the style prop, for center.
Without that all our progress circles will be lifted to the top of the component.

The component itself, should look like that then:

export const DeepProgressCircle = () => {
  return (
    <>
      <ProgressCircle
        style={{ height: 200 }}
        progress={0.8}
        progressColor={'rgb(134, 65, 244)'}
        startAngle={-Math.PI}
        endAngle={Math.PI}>
        <Svg style={{ justifyContent: 'center' }}>
          <ProgressCircle
            style={{ height: 180 }}
            progress={0.7}
            progressColor={'rgb(134, 65, 244)'}
            startAngle={-Math.PI}
            endAngle={Math.PI}>
            <Svg style={{ justifyContent: 'center' }}>
              <ProgressCircle
                style={{ height: 160 }}
                progress={0.6}
                progressColor={'rgb(134, 65, 244)'}
                startAngle={-Math.PI}
                endAngle={Math.PI}>
                <Svg style={{ justifyContent: 'center' }}>
                  <ProgressCircle
                    style={{ height: 140 }}
                    progress={0.5}
                    progressColor={'rgb(134, 65, 244)'}
                    startAngle={-Math.PI}
                    endAngle={Math.PI}>
                    <Svg style={{ justifyContent: 'center' }}>
                      <ProgressCircle
                        style={{ height: 120 }}
                        progress={0.4}
                        progressColor={'rgb(134, 65, 244)'}
                        startAngle={-Math.PI}
                        endAngle={Math.PI}>
                        <Svg style={{ justifyContent: 'center' }}>
                          <ProgressCircle
                            style={{ height: 100 }}
                            progress={0.3}
                            progressColor={'rgb(134, 65, 244)'}
                            startAngle={-Math.PI}
                            endAngle={Math.PI}
                          />
                        </Svg>
                      </ProgressCircle>
                    </Svg>
                  </ProgressCircle>
                </Svg>
              </ProgressCircle>
            </Svg>
          </ProgressCircle>
        </Svg>
      </ProgressCircle>
    </>
  )
}

The result is visible below:

deepProgressCircle

It’s very useful if we need to compare progress of the different actions, for example.

Colored bows

We have already studied a few useful cases, so let’s make a little art.
We will take the example from above, set the backgroundColor of the progress circles for white. Also we will work a bit with the startAngle and endAngle:

export const DeepProgressCircle = () => {
  return (
    <>
      <ProgressCircle
              style={{ height: 200 }}
              progress={0.8}
              backgroundColor={'white'}
              progressColor={'rgb(134, 65, 244)'}
              startAngle={-Math.PI}
              endAngle={Math.PI}>
        <Svg style={{ justifyContent: 'center' }}>
          <ProgressCircle
                  style={{ height: 180 }}
                  progress={0.7}
                  backgroundColor={'white'}
                  progressColor={'blue'}
                  startAngle={-Math.PI * 0.9}
                  endAngle={Math.PI * 1.1}>
            <Svg style={{ justifyContent: 'center' }}>
              <ProgressCircle
                      style={{ height: 160 }}
                      progress={0.6}
                      backgroundColor={'white'}
                      progressColor={'green'}
                      startAngle={-Math.PI * 0.8}
                      endAngle={Math.PI * 1.2}>
                <Svg style={{ justifyContent: 'center' }}>
                  <ProgressCircle
                          style={{ height: 140 }}
                          progress={0.5}
                          backgroundColor={'white'}
                          progressColor={'red'}
                          startAngle={-Math.PI * 0.7}
                          endAngle={Math.PI * 1.3}>
                    <Svg style={{ justifyContent: 'center' }}>
                      <ProgressCircle
                              style={{ height: 120 }}
                              progress={0.4}
                              backgroundColor={'white'}
                              progressColor={'orange'}
                              startAngle={-Math.PI * 0.6}
                              endAngle={Math.PI * 1.4}>
                        <Svg style={{ justifyContent: 'center' }}>
                          <ProgressCircle
                                  style={{ height: 100 }}
                                  progress={0.3}
                                  backgroundColor={'white'}
                                  progressColor={'yellow'}
                                  startAngle={-Math.PI * 0.5}
                                  endAngle={Math.PI * 1.5}
                          />
                        </Svg>
                      </ProgressCircle>
                    </Svg>
                  </ProgressCircle>
                </Svg>
              </ProgressCircle>
            </Svg>
          </ProgressCircle>
        </Svg>
      </ProgressCircle>
    </>
  )
}

As you can see in each subsequent case we reduced startAngle for 10%, and increased endAnglefor 10%. Thanks to that, each of our progress circle still have 360 deg, but is rotated 36 deg (10% we used to reduce startAngle and increased endAngle), to the right.
We also reduced each subsequent progress for 0.1. Thanks to that we got the rainbow bows:

rainbowBows

If we set progress prop of all of them for 0.8, we can perfectly see how each of the subsequent circles turned 36 deg to the right:

rainbowBows2

Pac-man

At the end of the ProgressCircle part, I just couldn’t resist to create Pac-man :) so here it is:

import { ProgressCircle } from 'react-native-svg-charts'

export const Pacman = () => {
  return (
    <>
      <ProgressCircle
        style={{ height: 200 }}
        progress={0.79}
        strokeWidth={100}
        cornerRadius={0}
        backgroundColor={'white'}
        progressColor={'#ffe737'}
        startAngle={-Math.PI * 1.3}
        endAngle={Math.PI * 0.7}
      />
    </>
  )
}
pac man

Pie chart props summary

Let’s summarize the props we’ve already used in the Progress circle:

  • style - like always we need to set the height, to put our svg elements on something. Of course, we can set a lot of other properties available in the CSS sheets
  • progress - progress we want to display. We set it in the range 0-1, which corresponds to values from 0% to 100%.
  • progressColor - color of progressBar. Not the background but the progress line itself.
  • startAngle - a point on the circle, where the progress bar starts (number of degrees). Default setting: Math.PI
  • endAngle - a point on the circle, where the progress bar ends (number of degrees). Default setting: -Math.PI
  • cornerRadius - this prop is responsible for the angle of our line
  • strokeWidth - it’s literally the width of our progress line
  • backgroundColor - the color of the circle below the line (circle in the background, under progress line)

##Axes

So far we have gone through 5 types of charts:

  • Area Chart
  • Bar Chart
  • Line Chart
  • Pie Chart
  • Progress Circle

For sure, you already noticed, maybe asked yourself, where are the axes?
Of course, we don’t use them in the Pie Chart or Progress Circle (we can create our default labels for them). But with the first three kinds of charts, they are almost inseparable.

In the react-native-svg-charts, axis X and axis Y are separated components. In fact, they can be so troublesome sometimes, that I decided to devote a separate chapter to them.
Let’s dive into them and talk about those possibly problematic parts, and how to get through them. We will use one of the previously prepared and explained components, to wrap it up inside axes.

Axis Y

To display the axis Y ‘on’ our chart, we have to follow a few rules. The first of them is that the chart and the axis have to be places in the same wrapping container (for example in View).
Another one is that we need to pass basic data to the axis. Thanks to that, component will know how to render proper axis for the chart. Which data exactly?
We need to consider two basic situations:

  • When we pass an number[] to the chart. For example:
const data = [80, 10, 95, 48, 24, 67, 51, 12, 33, 0, 24, 20, 50]
  • That’s the most basic situation, and we just need to pass the same data array to the axis (I will show you how soon). We pass the same data array to the chart and to the axis.
  • When we had to construct objects[]. We already did that many times in this tutorial. For example when we were creating LineChart with the average value:
export const composeDataWithAverageValue = (
 valuesArray: number[],
 averageValuesArray: number[]
 ) => {
   return [
     {
      data: valuesArray,
      svg: { strokeWidth: 3 },
     },
     {
      data: averageValuesArray,
      svg: { strokeWidth: 1.5, strokeDasharray: [8, 16] },
     },
   ]
 }
const dataWithAverageValue = composeDataWithAverageValue(data2, averageValuesArray)

Now the construction is more complicated, but what we have to do is very simple. We just need to pass to the axis, number[] which we used as a base array. In case above it was data2. All we have to do, is to pass this basic number[] to the axis (I will show you how soon).

What I have already explained is in fact the data props, we pass to the axis. So, let’s list and explain props we pass to the axis Y:

  • data - number[]. Array of basic values we provided to the chart (or eventually used, to create more complicated structure that was then passed to the chart)
  • formatLabel - function we pass to set, how exactly values on the axis Y should be displayed. For example if we want to show speed in km/h, we can pass the function “(value) => ${value} km”, and values on the axis Y will be displayed with the additional ‘km/h’. Of course, we can just do nothing and don’t pass our custom value. In that case we will get bare numbers on the axis Y.
  • numberOfTicks - default value - 10. Simply, number of the values displayed on the axis Y. If we set it for 2 just two values - max and nim will be displayed on the axis, without intermediate values. Of course, the axis is divided proportionally into the appropriate number of parts.
  • style - well known line-style prop. In this case it refers to the container for the axis Y.
  • svg - this prop is a style-prop for the axis itself. It can take several properties. A full list of them is available HERE
  • contentInset - object with four values: top, bottom, left and right. It sets distance between wrapping component and axis border.
  • min - works exactly the same as the gridMin prop in the charts. Sets the minimal value, of the axis. It HIGHLY RECOMMENDED to set here the same value, as we did for the gridMin in the chart, we want to display with the axis. In other case axis will be not proportional to the chart. Of course if we didn’t set gridMin for the chart, you also shouldn’t do that with min for the axis.
  • max - works on the same principle as min above, but with maximal value and gridMax.

HINT: If you put axisY above the chart component in the wrapper container, axisY will be displayed on the left side of the chart. If you do the opposite, it will be displayed on the right side of the chart.

With this knowledge, we can now take the previously prepared chart, put it in the container, and inject our axis Y component there:

import React from 'react'
import { data2 } from '@/data/data'
import { ChartContainer } from './BasicAxisY.styled'
import { YAxis } from 'react-native-svg-charts'
import { LineChartWithAverage } from '@/screens/LineChartComponent/LineChartWithAverage/LineChartWithAverage'

export const BasicAxisY = () => {
  return (
    <ChartContainer>
      <YAxis
        data={data2}
        contentInset={{ top: 20, bottom: 20 }}
        min={-50}
        max={150}
        svg={{
          fill: 'grey',
          fontSize: 11,
        }}
        style={{ marginRight: 5 }}
        formatLabel={(value) => `${value} km`}
      />
      <LineChartWithAverage />
    </ChartContainer>
  )
}

I think the settings above should be perfectly clear for us. fontSize property, used in the svg prop, of course refers to size of our values, displayed in the axis Y. Also we didn’t set numberOfTicks, because I wanted to leave the default value. Why we set the contentInset for { top: 20, bottom: 20 }? Because the chart have it set for the same values.
Also, I used the styled components here. To avoid confusion, you can find style attributes for the ChartContainer (which is in fact just styled View component), here:

import styled from 'styled-components/native'

export const ChartContainer = styled.View`
  flex-direction: row;
  margin-vertical: ${({ theme }) => theme.spacing.xl}px;
  position: relative;
`

The result is visible below:

basicAxisY

Axis Y Line

I’m aware that one question is in the air: “If it’s an axis… so where is the axis line…?“.
For sure that was my first question, when I used axisY from the react-native-scg-charts library for the first time.
The answer will probably not satisfy you, but as far I’m 99% sure, there is no props responsible for drawing it. Values just hang in the air.
What can we do then? We can simply draw our custom line and put it on the chart. I already did it for you, and you can find the solution below:

import React from 'react'
import { data2 } from '@/data/data'
import { ChartContainer } from './AxisYWithLine.styled'
import { YAxis } from 'react-native-svg-charts'
import { Arrow, AxisYLine } from '@/screens/Axes/AxisYWithLine/AxisYWithLine.styled'
import { LineChartWithAverage } from '@/screens/LineChartComponent/LineChartWithAverage/LineChartWithAverage'

export const AxisYWithLine = () => {
  return (
    <>
      <ChartContainer>
        <YAxis
          data={data2}
          contentInset={{ top: 20, bottom: 20 }}
          min={-50}
          max={150}
          svg={{
            fill: 'grey',
            fontSize: 11,
          }}
          style={{ marginRight: 5 }}
          numberOfTicks={5}
          formatLabel={(value) => `${value} km`}
        />
        <AxisYLine />
        <Arrow />
        <LineChartWithAverage horizontalGrid />
      </ChartContainer>
    </>
  )
}

Like you can see we inserted AxisYLine and Arrow to the container, between axisY and LineChartWithAverage component. Like I said I’m using styled components, but there is nothing to be scared of if you didn’t used it before.
Let’s take a look how the AxisYLine and Arrow elements are created:

import styled from 'styled-components/native'

export const ChartContainer = styled.View`
  flex-direction: row;
  margin-vertical: ${({ theme }) => theme.spacing.xl}px;
  position: relative;
`

export const AxisYLine = styled.View`
  height: 243px;
  width: 1px;
  position: absolute;
  left: 42px;
  bottom: 32px;
  background-color: lightgrey;
`

export const Arrow = styled.View`
  height: 5px;
  width: 5px;
  position: absolute;
  left: 40px;
  top: 23px;
  border-left-width: 1px;
  border-left-color: grey;
  border-top-width: 1px;
  border-top-color: grey;
  transform: rotate(45deg);
`

I hope I don’t need to explain style properties I used, but for those who never used styled components before:
I encourage you with all my heart, to use styled components because coding is so much more enjoyable with it, BUT YOU DON’T HAVE TO of course. In place of the AxisYLine in the AxisYWithLine component, you can just use View component and style it using your own method (line-styling, scss sheet, styleShit etc.)

What I should also mention is that I have set horizontal grid in the chart component, and numberOfTicks for 5. Let’s see what we got:

axisYWithLine

Axis X

The XAxis topic is very similar to the axisY topic. In fact, we have only few differences, and most important of them are very simple:

  1. Not the values, but indexes from the array are displayed on the XAxis
  2. We don’t have the min and the max props, because we display indexes here, so the values are hard.

Apart from the above, we use the same props for the XAxis as we did for the axisY.
We also create them in the same way:

import React from 'react'
import { data2 } from '@/data/data'
import { ChartContainer } from './BasicAxisX.styled'
import { XAxis } from 'react-native-svg-charts'
import { LineChartWithAverage } from '@/screens/LineChartComponent/LineChartWithAverage/LineChartWithAverage'

export const BasicAxisX = () => {
  return (
    <ChartContainer>
      <LineChartWithAverage />
      <XAxis
        style={{ marginHorizontal: -10 }}
        data={data2}
        contentInset={{ left: 10, right: 10 }}
        svg={{ fontSize: 10, fill: 'black' }}
      />
    </ChartContainer>
  )
}

The good practice is to set value of the contentInset for {{ left: 10, right: 10 }}. In other case, first and last values on the axis, will be not shown properly. To keep the proportion between the chart ans XAxis we have to set chart’s container horizontal padding for the 10px.
Our base XAxis is visible below:

basicAxisX

Axis X Line

We face the same problem, we had with the axisY: Where is the line? Of course, the solution is also the same, but like we know, the width of different devices can be really confusing. How to make a custom line, and make sure that the width of the line will always fit the axis and the chart’s width?
There are several ways to resolve that problem. I prefer to use onLayout event.
I will apply it to our new component and explain how it works below.
Just for fun I also inserted condition to the formatLabel prop. THanks to that, only 0 and indexes divisible by 4, will be displayed on the XAxis:

import React, { useState } from 'react'
import { data2 } from '@/data/data'
import { Arrow, AxisXLine, ChartContainer } from './AxisXWithLine.styled'
import { XAxis } from 'react-native-svg-charts'
import { LineChartWithAverage } from '@/screens/LineChartComponent/LineChartWithAverage/LineChartWithAverage'

export const AxisXWithLine = () => {
  const [componentWidth, setComponentWidth] = useState(0)

  return (
    <ChartContainer
      onLayout={(event) => {
        const { width } = event.nativeEvent.layout
        setComponentWidth(width)
      }}>
      <LineChartWithAverage />
      <XAxis
        style={{ marginHorizontal: -10, marginTop: 10 }}
        data={data2}
        formatLabel={(value, index) => {
          if (index % 4 === 0) {
            return index
          }
          return ''
        }}
        contentInset={{ left: 10, right: 10 }}
        svg={{ fontSize: 10, fill: 'black' }}
      />
      <AxisXLine width={componentWidth} />
      <Arrow />
    </ChartContainer>
  )
}

onLayout event can be very useful if we need to get information about the container we use in the application. For example, it’s width.
Because we haven’t set ChartContainer width, it takes up all the available space. Thanks to the onLayout we always get the current width. Now we just need to pass it to the AxisXLine component, set it as its width, and we can be sure, that line width will always fit our chart’s width:

interface LineProps {
  width: number
}

export const AxisXLine = styled.View<LineProps>`
  height: 1px;
  width: ${(props) => props.width}px;
  position: absolute;
  left: 0;
  bottom: 22px;
  background-color: lightgrey;
`

export const Arrow = styled.View`
  height: 5px;
  width: 5px;
  position: absolute;
  right: 0;
  bottom: 20px;
  border-left-width: 1px;
  border-left-color: grey;
  border-top-width: 1px;
  border-top-color: grey;
  transform: rotate(135deg);
`

The Arrow component don’t need any additional settings. Our new chart with XAxis:

axisXWithLine

HINT: If you put XAxis above the chart component in the wrapper container, XAxis will be displayed on the top of the chart. If you do the opposite, it will be displayed on the bottom of the chart. Usually the desired result is to display it below.

Axis X and Axis Y together

It is time to display both axes on the same graph. Of course, we can do it, but you need to know the trick if you want to do it properly.
I will first show the code, and then explain to you what exactly happened:

import { XAxis, YAxis } from 'react-native-svg-charts'
import { data2 } from '@/data/data'
import { LineChartWithAverage } from '@/screens/LineChartComponent/LineChartWithAverage/LineChartWithAverage'
import React, { useState } from 'react'
import {
  ChartContainer,
  HorizontalArrow,
  VerticalArrow,
  AxisXLine,
  AxisYLine,
} from './BothAxesWithLines.styled'
import { SafeAreaView } from 'react-native'

export const BothAxesWithLines = () => {
  const [componentWidth, setComponentWidth] = useState(0)

  return (
    <SafeAreaView
      style={{ flexDirection: 'row', flex: 1 }}
      onLayout={(event) => {
        const { width } = event.nativeEvent.layout
        setComponentWidth(width)
      }}>
      <YAxis
        data={data2}
        contentInset={{ top: 20, bottom: 20 }}
        min={-50}
        max={150}
        svg={{
          fill: 'grey',
          fontSize: 11,
        }}
        style={{ marginRight: 5, height: 300 }}
        numberOfTicks={10}
        formatLabel={(value) => `${value} km`}
      />
      <ChartContainer>
        <LineChartWithAverage />
        <XAxis
          style={{ marginHorizontal: -10, width: componentWidth, marginTop: 10 }}
          data={data2}
          formatLabel={(value, index) => index}
          contentInset={{ left: 10, right: 10 }}
          svg={{ fontSize: 10, fill: 'black' }}
        />
        <AxisXLine width={componentWidth} />
        <AxisYLine />
        <HorizontalArrow />
        <VerticalArrow />
      </ChartContainer>
    </SafeAreaView>
  )
}

Or main wrapper is now the SafeAreView. It the wrapper for the axisY and the ChartContainer. In turn the ChartContainer is the wrapper for the XAxis and chart itself.
Why? I will quote the authors’ explanation:

“Layout of an x-axis together with a y-axis is a problem that stems from flexbox. All react-native-svg-charts components support full flexbox and therefore all layout problems should be approached with the mindset “how would I layout regular Views with flex in this way”. In order for us to align the axes correctly we must know the height of the x-axis or the width of the x-axis and then displace the other axis with just as many pixels. Simple but manual.”

I will add from myself, that the height of YAxis have to be set for the same value as for the chart. In our example LineChart (LineChartWithAverage) and YAxis have the same height: 300px.
Also the width of the XAxis have to be set for the width of the main component. Best way is to use onLayout event, as we did in our example.
Otherwise, all values will be compressed into one point, and chart will be not displayed.

That’s how the full chart looks:

bothAxes

Summary

Like I said at the beginning of this article, you can find all the examples (and few more) in our repository:

React Native Chart Examples Library

As a bonus you will find there ScatteredChart section.
React-native-svg-charts is not offering this kind of solution for the users. That’s why i prepared my custom solutions especially for you.
Those examples and the functions provided for them, can be used in different situations, so I encourage you to check it by yourself.
I’m sure it will be useful for you in your work :)

I hope this article will help you with your work and encourage you to use charts in your projects. My idea was to explain everything that we used, because a good understanding of the topic enables us to play with it according to our specific needs. I hope that you’re now able to make your own unique charts and ‘beautify’ the application with them.

Introduction

Before we begin, I’d like to ask you just one question.
Have you ever used charts in your React Native project, especially with typescript?

If you did, then you know it’s not always easy, but you are also aware what impression it can make on users.
Writing about data in the block of text and showing it on a neat chart cannot be compared.l
Let me show you some detailed methods and examples.

If you’ve never used charts in your mobile app, this article will your step-by-step guide through the react-native-charts-svg library. It will also discuss some additional topics that I decided to touch upon. You will learn how to create really good-looking and useful charts simply, and most importantly – by yourself. You don’t need to worry about complicated parts, or blind spots when you have no idea what’s just happened. Why? Because I intend to make this topic plain and simple for anyone.
I’m pretty sure that the next time when you have to write about data in your project, you will use charts.

Repository

All the examples explained in this article (and few more) are available in the GitHub repository.
You can find it here:

React Native Chart Examples Library

Hope it will help you!

What will we learn

- How to create different chart types:

  • Area chart
  • Bar chart
  • Line chart
  • Pie chart
  • Progress circle
  • Scatter chart

- How to modify each part of the chart

- Add typescript types to the charts we will create

React-native-svg-charts setup

First, we need to add the library to our project using npm:

npm install --save react-native-svg-charts

or yarn

yarn add react-native-svg-charts

We are using typescript, so we also need to add types for it by npm:

npm install --save @types/react-native-svg-charts

or yarn

yarn add @types/react-native-svg-charts

Now we can start creating our first chart!

Area chart

Base example with props and typescript

Let’s start with the Area chart. First, we need to import it from the library:

import { AreaChart } from 'react-native-svg-charts'

If we implement it, just like that:

export const AreaChartScreen = () => {
    return <AreaChart />
}

We cannot see a thing there. We need to add at least 3 props:

  • data - an array of arbitrary data. It can be an array of numbers or objects. For now, let’s take it as an array of numbers.
  • style - a well-known line-style prop. In this case, it’s a style for the chart CONTAINER - not the chart itself. Height is set at a default 0, so we need to pass the expected height by ourselves. Always. Here we can treat the container not as a box, but more like a canvas, to write on. If we did not set the height, there is nothing to overlay the chart on.
  • svg - a style-prop for the chart. It can take several properties, but the most important one for the Area chart is fill. If we do not insert it, then technically we have our area chart set, but… it’s not visible.

With those 3 props:

import { AreaChart } from 'react-native-svg-charts'

const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]

export const AreaChartScreen = () => {
    return <AreaChart style={{ height: 200 }} data={data} svg={{ fill: '#ADD8E6' }} />
}

It might not be the most beautiful, but at least it’s ready to go:

areaChart base

Line with circle decorators and grid

Now let’s add some more features to our new chart. Our current props of interest are:

  • gridMin - minimal visible chart value. By default, it’s set for the minimal value of data. We can also set the value by ourselves, but if we set it a higher than the smallest data value, the system will take the minimal data value. For example, if the min value in the data array is -20, and we set it to 0, then the system will set it to -20. But if we set it at -40, then the system will set it to -40. In other words, the system always compares our value with the lowest value in the array and sets a lower one.
  • gridMax - maximal visible chart value. It works the same as gridMin, but in the opposite manner. The system always compares the value provided by us with the highest value from the data array and sets the higher one.
  • contentInset - an object with four values: top, bottom, left, and right. It sets the distance between the component and the chart border. For example, if we set { bottom: 30 }, then the distance between the component bottom border and the chart’s bottom border will be 30px. We can consider it as padding for the chart.

We will also add 3 additional components. Let’s call them ‘decorators’.

import { AreaChart, Grid } from 'react-native-svg-charts'
import { Dots, Line } from '@/screens/AreaChartScreen/chartAdds'

const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]

export const AreaChartScreen = () => {
    return (
        <AreaChart
            style={{ height: 200 }}
            data={data}
            gridMin={-100}
            gridMax={120}
            contentInset={{ top: 30, bottom: 30 }}
            svg={{ fill: '#ADD8E6' }}>
            <Grid />
            <Line />
            <Dots />
        </AreaChart>
    )
}
areaChart grid line dots

Even if the chart has the same height - 200px, it looks more flattened. That’s because the vertical range is no longer -80 to 95 (default min and max data values), but -100 to 120, just as we’ve set it. Also, we set the contentInset top and bottom for 30px.
The lines in the background are our Grid, the navy line marking the chart border is Line and the small black circles are Dots components.
Grid is a decorator we get with the react-native-svg-charts library, so we can just import it from there.
Let’s take a closer look at the structure of the other two:
Basic Line structure:

import { Path } from 'react-native-svg'

export const Line = ({ line }) => {
    return <Path key={'line'} d={line} stroke={'#0000BF'} fill={'none'} />
}

One closer look at the code, and we can see that there’s something wrong.

<Grid />
<Line />
<Dots />
export const Line = ({ line }) => { }

Component Line requires a ‘line’ prop, but we’re not passing any to it.
That will be no problem in JavaScript. Even if we don’t understand what’s just happened, we can leave it at that and go ahead. Because it’s working! YES, it is!
Of course, the right question is: WHY? And that’s what typescript is doing. It’s asking:
“Hey buddy, it’s working, great. But can you tell me, how that’s possible? You need to pass this prop or explain this weird behavior to me”.
So with the component created like that:

import { Path } from 'react-native-svg'

export const Line = ({ line }) => {
    return <Path key={'line'} d={line} stroke={'#0000BF'} fill={'none'} />
}

We get errors from typescript:

tsError
tsError2

Let’s first find out why it’s even working in the first place.
Like we can see, the Line component is placed inside the chart component. We had a specific reason to do that. Charts in the react-native-svg-charts library have their own specific lexical scope.
This means, the functions (components) inside them can use specific data provided for the chart.
Like we can see, we provided a ‘data’ array to the AreaChart component. It has been calculated by AreaChart and passed to the lexical scope, as a path string.
Line component used lexical scope and pulled ‘line’ prop from there.
To read more about lexical scope visit the MDN website - closures .

We finally know how it all works. So how can we handle it with typescript?
There are a few ways to do that, and the easiest one is to use Partial.

First we create an interface, which will take function props for our Line component.
We know that Path props ‘d’ is in fact the line path, so it is for sure type string. Thanks to the react-native-svg types we can also check it, to be sure:

pathProps

Let’s create interface LineProps, pass it to the Line component and make a type assertion.

import { Path } from 'react-native-svg'

interface LineProps {
    line: string
}

export const Line = (props: Partial<LineProps>) => {
    const { line } = props as LineProps
    return <Path key={'line'} d={line} stroke={'#0000BF'} fill={'none'} />
}

Now Line component doesn’t require line prop from us. line is still passed to the component, and it’s typed properly. Problem solved :) let’s study Path props, if we would like to modify them:

  • d - path of the drawn line
  • stroke - color of the circle border
  • fill - color of the circle background

Basic Dots structure:

import { Circle } from 'react-native-svg'

export const Dots = ({ x, y, data }) => {
    return (
        <>
            {data?.map((value, index) => (
                <Circle
                    key={index}
                    cx={x(index)}
                    cy={y(value)}
                    r={4}
                    stroke={'rgb(0, 0, 0)'}
                    fill={'white'}
                />
            ))}
        </>
    )
}

Like you’ve probably guessed, we’re facing the exact same problem as with the Line component. Fortunately, the solution is also the same:

import { Circle } from 'react-native-svg'

interface DecoratorProps {
    x: (arg: number) => number
    y: (arg: number) => number
    data: number[]
}

export const Dots = (props: Partial<DecoratorProps>) => {
    const { x, y, data } = props as DecoratorProps
    return (
        <>
            {data?.map((value, index) => (
                <Circle
                    key={index}
                    cx={x(index)}
                    cy={y(value)}
                    r={4}
                    stroke={'rgb(0, 0, 0)'}
                    fill={'white'}
                />
            ))}
        </>
    )
}

From the definition of the Circle component, we know that props cx and cy have to return number types. Also, the index and value will always be numbers. Now x and y typing are not hard. We also know that data is taken from the lexical scope of AreaChart so it has to be number[]. We have the circles on the chart. If we want to modify them, we need to know what props they are taking.

  • cx - coordinate on the axis x
  • cy - coordinate on the axis y
  • r - radius of the single circle
  • stroke - color of the circle border
  • fill - color of the circle background

Start prop

In this example, we will pay attention to only one prop:

  • start - number on the axis Y, from which area chart is painted.

This prop can dramatically change the perception of our chart. We still have the same data provided in our chart, but the start prop will be set for the -100. Let’s take a look:

import { AreaChart, Grid } from 'react-native-svg-charts'
import { Dots, Line } from '@/screens/AreaChartScreen/chartAdds'

const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]

export const AreaChartScreen = () => {
    return (
        <AreaChart
            style={{ height: 200 }}
            data={data}
            gridMin={-100}
            gridMax={120}
            start={-100}
            contentInset={{ top: 30, bottom: 30 }}
            svg={{ fill: '#ADD8E6' }}>
            <Grid />
            <Line />
            <Dots />
        </AreaChart>
    )
}
areaChartBottomArea

Even if the min value on the chart is -80, it looks like all the values are really height.

Let’s reverse it. We will set the start to 120, and get a blue sky above our values:

import { AreaChart, Grid } from 'react-native-svg-charts'
import { Dots, Line } from '@/screens/AreaChartScreen/chartAdds'

const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]

export const AreaChartScreen = () => {
    return (
        <AreaChart
            style={{ height: 200 }}
            data={data}
            gridMin={-100}
            gridMax={120}
            start={120}
            contentInset={{ top: 30, bottom: 30 }}
            svg={{ fill: '#ADD8E6' }}>
            <Grid />
            <Line />
            <Dots />
        </AreaChart>
    )
}
areaChartTopArea

Curve and gradient

In this example we will get rid of the Line and Circles components. And the start prop too. You know we can use them anytime in the line charts, but the visibility of other features will be better without them on the board. As mentioned in the title, our new features will be curve and gradient. So, what are they all about?
The curve is another prop:

  • curve - the shape of our area chart. The d3 library was used to allow the chart to take many different shapes. All the possibilities are listed HERE . There’s also plenty of information about the d3 library there. To pull some shapes from the library, we need to import the main object from d3 and rename it. shape will be a good choice. Now just pull the chosen shape from the shape object. For example curve={shape.curveNatural}.

The Gradient is another additional component:

  • Gradient - this additional component lets us change the color of the chart to the… gradient itself. Unfortunately inserting it inside the chart is not enough to make it work. We also need to remember to change the color of the fill property to 'url(#gradient)'. The svg prop will take the form svg={{ fill: 'url(#gradient)' }}

Let’s take a look at our code and chart after these changes:

import { AreaChart, Grid } from 'react-native-svg-charts'
import * as shape from 'd3-shape'
import { Gradient } from '@/screens/AreaChartScreen/chartAdds'

const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]

export const AreaChartScreen = () => {
    return (
        <AreaChart
            style={{ height: 200 }}
            gridMin={-100}
            gridMax={120}
            data={data}
            contentInset={{ top: 30, bottom: 30 }}
            curve={shape.curveNatural}
            svg={{ fill: 'url(#gradient)' }}>
            <Grid />
            <Gradient />
        </AreaChart>
    )
}
areaChart curve gradient

There are no more acute angles. They have been beautifully rounded. Also, the color has been set to the gradient.

So how is the Gradient component built?

import { Defs, LinearGradient, Stop } from 'react-native-svg'

export const Gradient = () => (
    <Defs>
        <LinearGradient id={'gradient'} x1={'0%'} y1={'0%'} x2={'0%'} y2={'100%'}>
            <Stop offset={'0%'} stopColor={'rgb(194, 65, 244)'} stopOpacity={0.8} />
            <Stop offset={'100%'} stopColor={'rgb(134, 65, 244)'} stopOpacity={0.2} />
        </LinearGradient>
    </Defs>
)

Here we use elements provided by react-native-svg:

  • Defs - this element is used to embed definitions that can be reused inside an SVG image.
  • LinearGradient - it’s a linear-gradient box, for which we can set from which point gradient should start and at which should end. x1, x2 are used to set gradient horizontal, and y1, y2 to set it vertical. Like we can see, in our case, it’s set from the top (where is 100%) to the bottom (0%).
  • Stop - this element is used to set border color. If we want to have a gradient consisting of 5 colors, we would use a new one for each color. offset property determines how to cover the element with color. In our example, we set the end result to 0%. This means that the rgb(194, 65, 244) color will be distributed from 100% at the beginning of the gradient to 0% at the end. stopColor is a color that we want to use, and stopOpacity is just its opacity.

We can also remove the Gradient, add the Line and change the curve to step form:

import { AreaChart, Grid } from 'react-native-svg-charts'
import { Line } from '@/screens/AreaChartScreen/chartAdds'

const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]

export const AreaChartScreen = () => {
    return (
        <AreaChart
            style={{ height: 200 }}
            gridMin={-100}
            gridMax={120}
            data={data}
            contentInset={{ top: 30, bottom: 30 }}
            curve={shape.curveStep}
            svg={{ fill: 'rgba(134, 65, 244, 0.5)' }}>
            <Line />
            <Grid />
        </AreaChart>
    )
}
areaChart steps

Multiple areas in one chart

Another option worth knowing is interpenetration of the areas. We can easily achieve this effect by putting one area chart on another. To do that we need to use Styleshit’s property absoluteFill.
This property allows us to create layers, by absolute positioning content in positions {top: 0, right: 0, bottom: 0, left: 0}.
I will also reverse the data array for the effect.

import { AreaChart, Grid } from 'react-native-svg-charts'
import { Line } from '@/screens/AreaChartScreen/chartAdds'

const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]

export const AreaChartScreen = () => {
    return (
        <ChartContainer>
            <AreaChart
                style={{ height: 200 }}
                gridMin={-100}
                gridMax={120}
                data={data}
                contentInset={{ top: 30, bottom: 30 }}
                curve={shape.curveNatural}
                svg={{ fill: 'rgb(124,252,0)' }}
            />
            <AreaChart
                style={StyleSheet.absoluteFill}
                gridMin={-100}
                gridMax={120}
                data={[...data].reverse()}
                contentInset={{ top: 30, bottom: 30 }}
                curve={shape.curveNatural}
                svg={{ fill: 'rgb(176,224,230, 0.8)' }}>
                <Grid />
            </AreaChart>
        </ChartContainer>
    )
}
areaChart multiArea

It can be useful sometimes, but I don’t recommend this kind of solution. If we want to compare data or other charts, like for example Line chart (we will talk about it later in this article), will be a much better fit.
To prove that, let me show you the anti-pattern. I will put only 3 areas (3 data arrays) on the same chart:

areaChart antiPattern

It looks horrible even on the big screen, but on the phone it’s actually completely illegible.

Area chart props summary

Let’s summarize the props we’ve already used in the Area charts:

  • data - an array of arbitrary data. For example, it can be an array of numbers or objects. For now, let’s take it as an array of numbers.
  • style - a well known line-style prop. In this case, it’s a style for the chart CONTAINER - not the chart itself. The height is set to a default 0, so we need to pass the expected height by ourselves. Always. Here we can treat the container not as a box, but more like a canvas, to write on. If we did not set the height, there is nothing we can put the chart on.
  • svg - a style-prop for the chart. It can take several properties, but the most important for the Area chart is fill. If we do not adjust it, then technically we have our area chart set, but… it’s not visible.
  • gridMin - minimal visible chart value. By default, it’s set to the minimal value of data. We can set the value by ourselves too, but if we set it at a higher value than the smallest data value, the system will take the smallest data value. For example, if the min value in the data array is -20, and we set it at 0, then the system will set it to -20. But if we set it at -40, then the system will set it to -40. In other words, the system always compares our value with the smallest value in the array and sets a lower one.
  • gridMax - maximal visible chart value. It works the same as gridMin, but in the opposite manner. The system always compares the value provided by us with the highest value from the data array and sets the higher one.
  • contentInset - object with four values: top, bottom, left and right. It sets the distance between the component’s border and the chart border. For example, if we set { bottom: 30 }, then the distance between the component bottom border and the chart bottom border will be 30px. We can consider it as padding for the chart.
  • curve - the shape of our area chart. The d3 library has been used here to allow the chart to take many different shapes. All the possibilities are listed HERE . There’s also more about the d3 library. To pull some shapes from the library, we need to import the main object from d3 and rename it. shape will be a good choice. Now just pull the chosen shape from the shape object. For example curve={shape.curveNatural}.
  • start - number on the axis Y, from which the area chart is painted. The area chart will be painted from this level on the axis X to each point on the chart.

In my opinion, those are most useful, but there are a few more we can use:

  • yAccessor - thanks to that function, we can provide a more complicated data structure and choose the value we want to use from keys in the objects array (used for axis Y) - we will use this prop later for other charts, so you will become familiar with it
  • xAccessor - thanks to that function, we can provide a more complicated data structure and choose the value we want to use from keys in the objects array (used for axis X) - we will use this prop later for other charts, so you will become familiar with it
  • yScale - A function that determines the scale of axis Y. As said in the documentation it’s not very well tested
  • xScale - A function that determines the scale of axis X. As said in the documentation it’s not very well tested
  • xMin - the minimal visible value of axis X. Rather redundant if you want to use all the available space to show data. Let’s imagine your data starts from 0. If you set it to -10, then you will get just empty white space on the left side. If you set it to 10, then part of your data will be invisible. Mobile devices are usually narrow. I recommend using the available space to the maximum to increase readability
  • xMax - the maximal visible value of axis X. The same situation like with xMin
  • yMin - the minimal visible value of axis Y. We can get the same effect using gridMin
  • yMax - the maximal visible value of axis Y. We can get the same effect using gridMan
  • numberOfTicks - number of lines visible on the background grid (of course only when we use additional component Grid)

Stacked area charts

The last topic in this section is the stacked area chart. This solution can be very useful, for example, if we want to show the sum of values in time. The idea is pretty straightforward - we put one value on another. For example, let’s say I have an orchard with apples, pears, cherries, and plums. I want to see how many kilograms of fruit I’ve picked’ in the last four years. I want to compare the values, but also see the difference in the sum in particular years.
A stacked area chart will be perfect for that.

First, we need to import StackedAreaChart from the react-native-svg-charts:

import { StackedAreaChart } from 'react-native-svg-charts'

To create the chart, we need data. The question is, – how do we get our hands on it? It’s obvious that we cannot just push number[]. When we were talking about data props in the first chart, I mentioned that data can be also an array of objects. That is the time to use that knowledge. StackedSreaChart is taking an array of objects as data props. Each object will be responsible for one year, and we have four kinds of fruits each year. So each year will be an object with 4 keys:

export const stackedAreaChartData = [
    {
        apples: 1840,
        pears: 1920,
        cherries: 960,
        plums: 400,
    },
    {
        apples: 1600,
        pears: 1440,
        cherries: 960,
        plums: 400,
    },
    {
        apples: 640,
        pears: 960,
        cherries: 1640,
        plums: 400,
    },
    {
        apples: 3320,
        pears: 480,
        cherries: 640,
        plums: 400,
    },
]

Obviously, we need to pass three values to make any chart visible: data, height, and fill (color). Now that we have our data, height is not a problem, but how do we handle different colors for different fruits?
We need to create an additional array with as many colors as here are keys (fruits in this case):

const colors = ['#8800cc', '#FF0000', '#0000FF', '#00FF00']

StackedAreaChart requires one additional prop. We need to pass keys props. Chart need to know on what variables it will be working. In other words, we need to provide an array with keys from our object (our fruits). Using typescript, we also need to pass the array of the proper type:

type Keys = ('apples' | 'pears' | 'cherries' | 'plums')[]

const keys: Keys = ['apples', 'pears', 'cherries', 'plums']

Now we can create our chart:

import { StackedAreaChart } from 'react-native-svg-charts'
import * as shape from 'd3-shape'
import { stackedAreaChartData } from '@/data/data'

type Keys = ('apples' | 'pears' | 'cherries' | 'plums')[]

export const StackedAreaChartComponent = () => {
    const colors = ['#8800cc', '#FF0000', '#0000FF', '#00FF00']
    const keys: Keys = ['apples', 'pears', 'cherries', 'plums']

    return (
        <StackedAreaChart
            style={{ height: 150, paddingVertical: 16 }}
            data={stackedAreaChartData}
            keys={keys}
            colors={colors}
            curve={shape.curveNatural}
        />
    )
}

Because colors are now picked from the array, handling them is the responsibility of different props. We need to pass the colors array to colors props. Also, the keys array has to be passed to the keys props, to inform the chart on what variables it will be working.
Remember, that we always need to have the same value of kays and colors. One color for one key. Let’s take a look at our chart now:

stackedAreaChart

Bar chart

Thanks to what we’ve already learned in the Area chart chapter, the Bar chart should be an easy part. Most of the props will be the same.
We will learn a few new things, but this chapter will mostly be base on the ready examples.
Let’s start with the first one.

Basic Bar chart

First we need to import BarChart from the react-native-svg-charts library:

import { BarChart } from 'react-native-svg-charts'

Now we can create base BarChart:

import { AreaChart, Grid } from 'react-native-svg-charts'
import { Dots, Line } from '@/screens/AreaChartScreen/chartAdds'

const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]

export const AreaChartScreen = () => {
    return (
        <BarChart
            style={{ height: 200 }}
            gridMin={-100}
            gridMax={120}
            data={data}
            svg={{ fill: 'rgb(134, 65, 244)' }}
            contentInset={{ top: 30, bottom: 30 }}>
            <Grid />
        </BarChart>
    )
}

We have well known props:

  • style
  • gridMin
  • gridMax
  • data
  • svg
  • contentInset

Nothing new here. Let’s see our chart:

baseBarChart

Spacing and Gradient

Now let me introduce you to two new props that occur only in the BarChart:

-spacingInner - space between the bars. Default set to 0.05 -spacingOuter - space outside the bars. In other words from the right and left border of the chart. We can consider it as a kind of padding inside the chart.

import { BarChart, Grid } from 'react-native-svg-charts'

const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]

export const BarChartScreen = () => {
    return (
        <BarChart
            style={{ height: 200 }}
            spacingInner={0.4}
            spacingOuter={0.1}
            gridMin={-100}
            gridMax={120}
            data={data}
            svg={{ fill: 'rgb(134, 65, 244)' }}
            contentInset={{ top: 30, bottom: 30 }}>
            <Grid />
        </BarChart>
    )
}

We just set the space between the charts to 0.4, which means that we set it at 40% of the FULL bar width. The width that bar would take if the spacingInner would be set to 0. Now the white space takes up 40% of that area and the bar takes up 60%. The same goes for spacingOuter. Now we set the distance from the left and right border to 10% of the original bar chart width. We also injected Gradient component which is literally the same as in Area chart:

barChart spacing gradient

yAccessor and different bar colors

As I mentioned when we were summing up props at the end of the Area chart part, we will now deal with the yAccessor. But first, let me explain how to create data, which will let us assign different colors to different bars on the same chart.

Again, we need to leave out convenient number[] and create an array of objects.
Each bar needs to have its own color. In other words, each object must have a key with a value and a key with color. The number of objects is identical to the number of bars we want to display. For the sake of our example, let’s say it will be five:

export const data6 = [
    {
        value: 50,
        svg: {
            fill: 'red',
        },
    },
    {
        value: 10,
        svg: {
            fill: 'orange',
        },
    },
    {
        value: 95,
        svg: {
            fill: 'purple',
        },
    },
    {
        value: 40,
        svg: {
            fill: 'blue',
        },
    },
    {
        value: 85,
        svg: {
            fill: 'green',
        },
    },
]

yAccessor will let us iterate over the chosen key. In this case, it will be the value. Now the svg prop will be taken automatically from each object. We will not pass it to the BarChart component then.

import { BarChart } from 'react-native-svg-charts'
import { data6 } from '@/data/data'

export const BarChartScreen = () => {
    return (
        <BarChart
            style={{ height: 200 }}
            spacingInner={0.1}
            gridMin={-10}
            gridMax={120}
            data={data6}
            yAccessor={({ item }) => item.value}
            contentInset={{ top: 30, bottom: 30 }}
        />
    )
}

Look for the props we pass. spacingOuter wasn’t really necessary, so we removed it. The same is true for svg. We don’t need it here, because each bar has its own svg object, and the chart will now take it from the objects.
The result looks like this:

barChartColored

We can now make a better definition for yAccessor:

  • yAccessor - we pass it a function that tells us - which key in the object array should be taken as a value to be displayed on the chart. In other words, we pass the object key the chart should iterate at. Another thing is that thanks to it, the chart iterating over the object’s array, can take other properties of the object (bar) and inject them into a specific bar.

Sounds logical? I hope so.

Dashed bars

Now, if the svg objects have a lot of properties to use, let’s have a bit of fun and create something unusual. Let me just remind you that the full list of svg object properties is available HERE .

First please take a look at what we want to achieve:

barChart dashed

It’s easier than you might expect. In fact, we don’t need to do a thing with the previously used BarChart component (I just made the chart a little bit taller for a better impression). The interesting part is the data array. We need to change svg object in each iteration to give us the expected effect:

export const data7 = [
    {
        value: 50,
        svg: {
            fill: 'white',
            stroke: 'red',
            strokeWidth: 1,
            strokeDasharray: [30, 5],
        },
    },
    {
        value: 10,
        svg: {
            fill: 'white',
            stroke: 'orange',
            strokeWidth: 2,
        },
    },
    {
        value: 95,
        svg: {
            fill: 'white',
            stroke: 'purple',
            strokeWidth: 2,
            strokeDasharray: [4, 2],
        },
    },
    {
        value: 40,
        svg: {
            fill: 'white',
            stroke: 'blue',
            strokeWidth: 1,
            strokeDasharray: [14, 4],
        },
    },
    {
        value: 85,
        svg: {
            fill: 'white',
            stroke: 'green',
            strokeWidth: 2,
            strokeDasharray: [2, 1],
        },
    },
]

What just happened?
In fact not much. We just added a few properties to the svg objects:

  • fill - background color of each bar. We set it as transparent because… it should be transparent.
  • stroke - this property is the borderline color.
  • strokeWidth - the borderline width
  • strokeDasharray - we use this only when we want to make our line dashed. This property gets an array with two numbers. The first one informs the chart about hte length of pieces of our borderline. The second one represents the length of spaces between them.

Of course, you can get even more creative and create even more interesting examples using svg possibilities.

Grades ( low, average, height )

For now, we will leave styling be, and focus on simple logic. We want to color our bars depending on the score or grade. Let’s stick with the same BarChart component, and work with data again.
If we want to color the bars depending on the score, should we give each bar its own color manually? Of course not. We will prepare a simple function that will do it for us instead.

We should remember that we’re working with typescript all this time. It’s not really visible, because of the types we added to our project at the beginning. The function we’re putting together right now will be typed by us.
Here is it:

export const transformDataForBarChart = (
    number: number
): { value: number; svg: { fill: string; stroke?: string; strokeWidth?: number } } => {
    // random ranges > number < inscribed to distinguish colors
    if (number <= 30) {
        return {
            value: number,
            svg: {
                fill: 'red',
            },
        }
    } else if (number > 30 && number < 70) {
        return {
            value: number,
            svg: {
                stroke: 'orange',
                strokeWidth: 2,
                fill: '#FFD580',
            },
        }
    } else {
        return {
            value: number,
            svg: {
                fill: 'green',
            },
        }
    }
}

Like we can see, this function takes a number(value) as an argument and returns the prepared object. fill property, is required because we always want to color our bar. But depending on the score, sometimes we want to give the bar a borderline. That’s why properties border and borderWidth are optional.
We depend on simple conditions:

  • if the score is lower or equal to 30, the bar will become red
  • if the score is lower than 70 but higher than 30, the bar will become orange
  • if the score is higher or equal to 70, the bar will become green ( it’s a score. The minimal value is 0, so we can use else)

Now we can use this function and create the chart:

import { BarChart } from 'react-native-svg-charts'

const data5 = [10, 20, 60, 30, 5, 90, 21, 47, 68, 88, 96, 55, 10, 73]

export const BarChartScreen = () => {
    const dataWithPickedColors = data5.map((item) => transformDataForBarChart(item))

    return (
        <BarChart
            style={{ height: 200 }}
            spacingInner={0.1}
            gridMin={-10}
            gridMax={120}
            data={dataWithPickedColors}
            yAccessor={({ item }) => item.value}
            contentInset={{ top: 30, bottom: 30 }}
        />
    )
}

Using the map, we iterate over each value and transform it to the required object. When it’s done, we assign a new array of objects to the dataWithPickedColors variable. The last step is simple - just pass it to the data prop. Here is the result:

barChart scores

Of course, you can make much more complicated functions and examples. In fact, you can do basically anything that you need at a particular moment.

Horizontal Bar chart

Until now, we’ve made few examples with classic BarChart. Another prop I want you to become acquainted with is horizontal.

  • horizontal - this props can take only two values, true or false. By default, it’s false. If it’s true, our chart will be rotated by 90 degrees.

If we would like to add the Grid component to our chart, we can also rotate it.
Just add direction={Grid.Direction.VERTICAL}.
There is nothing more to talk about here. It can be very useful, but it’s also super easy.

import { BarChart } from 'react-native-svg-charts'

const data8 = [10, 20, 60, 30, 5, 90]

export const BarChartScreen = () => {
    return (
        <BarChart
            style={{ height: 200, marginVertical: 20 }}
            horizontal={true}
            spacingInner={0.1}
            gridMin={0}
            gridMax={100}
            data={data8}
            svg={{ fill: 'lightblue' }}
            contentInset={{ top: 30, bottom: 30 }}>
            <Grid direction={Grid.Direction.VERTICAL} />
        </BarChart>
    )
}
barChart rotated

Stacked Bar chart

The rule is exactly the same as with Stacked Area Chart .

First, we need to create an array of objects with particular keys:

export const stackedBarChartData = [
    {
        apples: 1840,
        pears: 1920,
        cherries: 960,
        plums: 400,
    },
    {
        apples: 1600,
        pears: 1440,
        cherries: 960,
        plums: 400,
    },
    {
        apples: 640,
        pears: 960,
        cherries: 1640,
        plums: 400,
    },
    {
        apples: 3320,
        pears: 480,
        cherries: 640,
        plums: 400,
    },
]

Next, arrays with colors and keys:

type Keys = ('apples' | 'pears' | 'cherries' | 'plums')[]

const colors = ['#142459', '#FF0000', '#0000FF', '#00FF00']
const keys: Keys = ['apples', 'pears', 'cherries', 'plums']

And pass them to proper props in the StackedBarChart component:

import { StackedBarChart } from 'react-native-svg-charts'
import { stackedBarChartData } from '@/data/data'

type Keys = ('apples' | 'pears' | 'cherries' | 'plums')[]

export const StackedBarChartComponent = () => {
    const colors = ['#8800cc', '#FF0000', '#0000FF', '#00FF00']
    const keys: Keys = ['apples', 'pears', 'cherries', 'plums']

    return (
        <StackedBarChart
            style={{ height: 200, paddingVertical: 16 }}
            data={stackedBarChartData}
            keys={keys}
            colors={colors}
            contentInset={{ top: 30, bottom: 30 }}
        />
    )
}
stackedBarChart

Bar chart props summary

Let’s summarize props we already used in Bar charts:

  • data - an array of arbitrary data. It can be an array of numbers or objects.
  • style - a well known line-style prop. In this case, it’s a style for the chart CONTAINER - not the chart itself. Height is set at default 0, so we need to pass the expected height by ourselves. Always. Here we can treat the container not as a box, but more like a canvas, to write on. If we did not set the height, there is nothing we can put the chart on.
  • svg - a style-prop for the chart. It can take several properties. A full list is available HERE
  • gridMin - minimal visible chart value. By default, it’s set at the minimal value of the data. We can set the value by ourselves too, but if we set it at a higher value than the smallest data value, the system will take the smallest data value. For example, if the min value in the data array is -20, and we will set it to 0, then the system will set it to -20. But if we set it at -40, then the system will set it to -40. In other words, the system always compares our value with the smallest value in the array and sets a lower one.
  • gridMax - maximal visible chart value. Works the same as gridMin, but opposite. The system always compares the value provided by us with the highest value from the data array and sets the higher one.
  • contentInset - object with four values: top, bottom, left and right. It sets the distance between the component border and the chart border. For example, if we set { bottom: 30 }, then the distance between the component’s bottom border and the chart’s bottom border will be 30px. We can consider it as padding for the chart.
  • spacingInner - space between the bars. Default set for 0.05.
  • spacingOuter - space outside the bars. In other words from the right and left border of the chart. We can consider it as a kind of padding inside the chart.
  • yAccessor - we pass it a function that tells us, which key in the object’s array should be taken as a value to display on the chart. In other words, we pass the object key the chart should iterate at. Another thing is that thanks to it, chart iterating over the object’s array, can take other properties of the object (bar) and inject them into a specific bar.
  • horizontal - this props can take only two values, true or false. By default, it’s false. If it’s true, our chart will be rotated by 90 degrees.

We can use also:

  • xAccessor - thanks to this function, we can provide a more complicated data structure and choose the value we want to use from the keys in the objects array (used for axis X) - we will use this prop later for other charts, so you will get familiar with it
  • yScale - a function that determines the scale of axis Y. As said in the documentation it hasn’t been very well tested
  • xScale - a function that determines the scale of axis X. As said in the documentation it hasn’t been very well tested
  • xMin - the minimal visible value of axis X. Not really handy if you want to use all the available space to show data. Let’s imagine your data starts from 0. If you set it at -10, then you will get nothing more but an empty white space on the left side. If you set it to 10, then part of your data will not be visible. Mobile devices are usually narrow. I recommend using the available space to the maximum, to increase readability
  • xMax - the maximal visible value of axis X. The same situation as with xMin
  • yMin - the minimal visible value of axis Y. We can get the same effect using gridMin
  • yMax - the maximal visible value of axis Y. We can get the same effect using gridMan
  • numberOfTicks - number of lines visible on the background grid (of course only when we use an additional component Grid)

(Remember that some props are available only in a specific type of chart. For example, you cannot use start in the BarChart, and horizontal is not available for AreaChart)

Line chart

The line chart is very straightforward, which is probably its greatest advantage. We can put many different lines on the chart, and it will still remain legible. Of course, we still have a lot of possibilities to change it in the way we would like to see it.
Let’s check our options :)

Basic Line Chart

We already know most of the props that we have at our disposal. If you have some doubts, please check the summary section again. Our first Line Chart is a basic variant just with the Grid. Let’s import it from the react-native-svg-charts, add required props, and take a look at the result:

import { LineChart, Grid } from 'react-native-svg-charts'

export const LineChartComponent = () => {
    const data2 = [80, 10, 95, 48, 24, 67, 51, 12, 33, 0, 24, 20, 50]

    return (
        <LineChart
            style={{ height: 200 }}
            gridMin={-20}
            gridMax={120}
            data={data2}
            svg={{ stroke: 'rgb(134, 65, 244)' }}
            contentInset={{ top: 20, bottom: 20 }}>
            <Grid />
        </LineChart>
    )
}

As you probably notice, there is one difference inside the svg prop. But if you think about it, it’s completely logical. That far we were working on the area, or bars we were coloring, and a stroke was optional. Here the stroke is actually the part we want to show. It is our line. If we use fill property, then we will get some kind of AreaChart in fact. The stroke is the property we’re working on here.

LineChart base

Line shadow

Let’s add the shadow to our line. In order to do that we need to add another additional component - Shadow.

  • Shadow - this additional component lets us add the shadow to the Line chart. In fact, it’s another line made to look like a shadow.

We will now get rid of the Grid component and add Shadow component to our chart. Of course, we can use both if we like to.

import { LineChart } from 'react-native-svg-charts'
import { Shadow } from './chartAdds'

export const LineChartComponent = () => {
    const data2 = [80, 10, 95, 48, 24, 67, 51, 12, 33, 0, 24, 20, 50]

    return (
        <LineChart
            style={{ height: 200 }}
            gridMin={-20}
            gridMax={120}
            data={data2}
            svg={{ stroke: 'rgb(134, 65, 244)' }}
            contentInset={{ top: 20, bottom: 20 }}>
            <Shadow />
        </LineChart>
    )
}
lineChart shadow

Like I said before, Shadow is a line. That’s how it’s built:

interface LineProps {
    line: string
}

export const Shadow = (props: Partial<LineProps>) => {
    const { line } = props as LineProps
    return (
        <Path
            key={'shadow'}
            y={2}
            x={1}
            d={line}
            fill={'none'}
            strokeWidth={4}
            stroke={'rgba(134, 65, 244, 0.2)'}
        />
    )
}

Once again we had to face the lexical scope of the chart component. Fortunately, we already know how to handle. Once again, we use the Path component. If you want to remind yourself about all Path props, go to the section Line, circle decorators and grid . So… what’s new?

  • y - props that allow us to change the position of the line on axis Y.
  • x - props that allow us to change the position of the line on axis X.

In the Line component, we also didn’t have strokeWidth applied, but it’s the props that we know already.

Line round shape, grid and gradient

In this example, we will add Grid and Gradient components, and round the chart angles. Also, we will make the line a little wider to make the gradient more visible. Like I’ve said already, the stroke property is responsible for color, so we have to inject url(#gradient) there.

import { LineChart, Grid } from 'react-native-svg-charts'
import { Shadow, Gradient } from './chartAdds'

export const LineChartComponent = () => {
    const data2 = [80, 10, 95, 48, 24, 67, 51, 12, 33, 0, 24, 20, 50]

    return (
        <LineChart
            style={{ height: 200 }}
            gridMin={-20}
            gridMax={120}
            data={data2}
            curve={shape.curveNatural}
            svg={{
                strokeWidth: 2,
                stroke: 'url(#gradient)' }}
            contentInset={{ top: 20, bottom: 20 }}>
            <Shadow />
            <Grid />
            <Gradient />
        </LineChart>
    )
}
lineChart curve shadow gradient
Typescript
React Native
Conrad Gauza
No items found.
Do you need help with developing react solutions?

Leave your contact info and we’ll be in touch with you shortly

Leave contact info
Become one of our 10+ ambassadors and earn real $$$.
By clicking “Accept all”, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information.