Actual Gradient Borders in React Native (the almighty Masked View)
Tags: reactnative
Published: 2022-08-28 19:55:00

(Just want the package? [check it out here](https://github.com/iway1/react-native-gradient-border))

If you Google "React Native gradient border" like I did you may find yourself a little disappointed.

I needed to make something like this:

![The goal gradient](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wnhz7ktnnu49xr2l89wx.png)

I needed just a gradient border with a transparent background, but when I searched the internet for solutions all that showed up in my search had something like:

```jsx
<LinearGradient
    style={{
      height: 150,
      width: 200,
      borderRadius: 20,
    }}
>
    <View
        style={{
          borderRadius: 15,
          flex: 1,
          margin: 5,
          backgroundColor: '#fff',
          justifyContent: 'center',
        }}
    >
       {/* some other stuff */}
    </View>
</LinearGradient>
```

Which might look something like:

![Fake gradient border](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kr2tlrqsvdo19nlyhgaf.png)

This is simulates a gradient border but it is not a gradient border. It is a full on gradient view hidden behind some other view with a certain background color. 

This solution can work sometimes, but it isn't a universal solution and it's a bit janky to work with (because making changes becomes more difficult since we're adjusting multiple props in both the parent and child, and it requires two views to have their backgrounds in sync).

If you needed a non-static background, like say you wanted to show an image like I needed too, none of these solutions would work at all (because, again we're needing two views to have matching backgrounds).

So, I was discouraged, but then I came across a library that could easily allow me to implement a true gradient border.

## Masked Views to the rescue 💯
`@react-native-masked-view/masked-view` is an **incredibly** powerful library. CSS has view masking if you're in web dev as well, but for React Native we will need a library (of course). Luckily `@react-native-masked-view/masked-view` is well maintained and even used by `@react-navigation`

### What's masking
Masking applies one view as a mask to another target view, which means it takes the opacity values from the mask view view and applies them to the target view.

That may not sound like much, but it can be used to create absolutely amazing visuals if used correctly. Here's a really simple example with some really cool text effects:

![Mask Gradient Text Effects](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0t14y8n581cl01pcpkfp.png)

The masked view is great because it's both simple to use once you know how it works, but also has huge potential for creating compelling visuals in your UI.

You can think of it as sort of "cutting out a slice" of one view in the shape of another view, but it also works with any opacity value including gradients. So you can create things like this, too, using an image plus a gradient:

![Gradient Image Mask](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5kcpss5wxx7c8fcob4sr.png)

So in our React Native app, if we wanted to create a gradient giraffe cutout with `@react-native-masked-view/masked-view`, we can do:
```jsx
<MaskedView
  style={{ flex: 1 }}
  maskElement={(
    <View
      style={[StyleSheet.absoluteFill]}
    >
      <ImageBackground
        style={[StyleSheet.absoluteFill]}
        resizeMode='contain'
        source={require('./giraffe.png')}
      />
    </View>

  )}
>
  <LinearGradient
    colors={['red', 'blue']}
    style={StyleSheet.absoluteFill}
    pointerEvents='none'
  />
</MaskedView>
```
![Giraffe Mask View](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdztweemqj4ut5o51tf1.png)
 
Here the mask is a giraffe shape, and the target view is a gradient, so we get a gradient giraffe. Note that the actual image has a transparent background, which is very important when masking things because, again, masks apply only the opacity of the image to the target element (LinearGradient).

By the way, I'm not saying that you should or shouldn't have a gradient giraffe in your app, I'm just trying to make the point the `MaskedView` can apply the opacity values from any mask element to any target view. The actual image looks like this:

![Original giraffe image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hd6k9gj06zcqviofmoaa.png)

Notice the colors of the image don't actually matter, just the opacity.
  
Anyways, we don't need to get that fancy for what we're doing. 

All we need to do is create a border-shaped mask and apply it to a `LinearGradient`. Seems so simple when you think about it. In `@react-native-masked-view/masked-view` it's just a little bit of code.

## 💯 A universal solution to gradient borders 💯

```jsx
<MaskedView
    maskElement={(
        <View
            pointerEvents='none'
            style={[
                StyleSheet.absoluteFill, 
                { borderWidth, borderRadius }]}
        />
    )}
    style={[StyleSheet.absoluteFill]}
>
    <LinearGradient
        colors={['red', 'orange']}
        pointerEvents='none'
    />
</MaskedView>
```
This component would go inside the component to which we want to apply a gradient border. Notice the absolute fill style, this will make sure it applies the borders to the edge of the immediate parent view.

![First Attempt](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yf1av3kkui5tnf84qtmy.png)
 

It's actually kind of hilarious how much less code this is to achieve a better result than the solutions that you'll find when you google this question.

### Issues
The main issue with this approach is that it doesn't adjust the layout to compensate for the border. For example, if you don't apply padding to the parent view the gradient border can overlaid on top of components:

![Overlaid Gradient](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tm74wvq7wzzerrkoys92.png)
 
So we'll need to apply additional padding to compensate:
```jsx
style={{
  width: 200,
  height: 200,
  padding: 20,
}}
```
![Fixed Overlay With Padding](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r2kkej7wyanujd0ywf4p.png)
 
That can be a pain to do, because when changing the border width we'd also need to change the view's padding. 

### 🤔 How do we solve this once and for all?

As is often possible in React (Native), we can create reusable components with a great APIs that do all of the tedious stuff for us, and then in the future we can reach for it by default! We just need to make sure it's well-thought out enough to support any future use cases.

SO, we can create a reusable component that automatically applies padding, which is what I've done in my package `@good-react-native/gradient-border`:
```jsx
<GradientBorderView
  gradientProps={{
    colors: ['red', 'blue']
  }}
  style={{
    borderWidth: 5,
    width: 200,
    height: 200,
  }}
>
  <Text>
    Automatic Padding!
  </Text>
</GradientBorderView>
```

![Gradient Border package example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ajhrfizm1pkz9ztytfov.png)

Notice the `gradientProps` prop getting passed to `<GradientBorderView/>`. That can have any prop that `expo-linear-gradient`'s `<LinearGradient/>` component supports, and it's typesafe! Sweet. That means we can get creative with our settings:
```jsx
gradientProps={{
  colors: ['red', 'orange', 'blue', 'green'],
  start: { x: 0.5, y: 0, },
  end: { x: 1, y: 1 }
}}
```

I did my best to try and make sure the `GradientBorderView` behaves exactly like a normal view, except it has a gradient border. That means it supports any of the typical border styles and we can change the border radius of individual corners as well as the width of sides: 
 
```jsx
style={{
  justifyContent: 'center',
  alignItems: 'center',
  width: 100,
  height: 100,
  borderLeftWidth: 5,
  borderTopLeftRadius: 10,
  borderRightWidth: 5,
  borderBottomRightRadius: 5,
  borderBottomWidth: 2,
  borderBottomLeftRadius: 20,
  borderTopWidth: 0.25
}}
```

![True Gradient Border](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x0m942rc406z9gwkdzg1.png)

Like I said, you don't need to do any of this yourself, you can just [download my package](https://github.com/iway1/react-native-gradient-border) to create an actual gradient border.

### This is only the beginning
The last thing I want to say is that this is just one potential application of `@react-native-masked-view/masked-view`, there are so many other possibilities with this tool! 

For example something like this fade away scroll view where components fade out as they get near the top of the view:

![Fade Out View](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vgp1b1b4whg3o4q8i4r6.png)

It's really powerful, so get creative! 

Thanks for reading I really do appreciate it ❤️