ReactNative: using SVG as a button
Just recently I started using ReactNative. I've been using React for years but this thing is a whole new world. There are tons of basic stuff which I don't know how to do. Using an SVG as a button is one of them. Find my solution below and expect more short #reactnative blog post soon.
First, we should mention that we can't just copy-paste the SVG code in our ReactNative project and use it. We have to use react-native-svg package. My SVG for example looks like this:
import Svg, { Path } from "react-native-svg";
const lineProps = {
strokeOpacity: 1,
strokeWidth: 1,
strokeLineCap: "round",
strokeLineJoin: "round",
};
function Button({ size }) {
return (
<Svg width={size} height={size} viewBox="0 0 122.88 122.88" {...lineProps}>
<Path d="M61.44,0c16.966,0,32.326,6.877,43.445,17.995s17.996,26.479,17.996,43.444c0,16.967-6.877,32.327-17.996,43.445 S78.406,122.88,61.44,122.88c-16.966,0-32.326-6.877-43.444-17.995S0,78.406,0,61.439c0-16.965,6.877-32.326,17.996-43.444 S44.474,0,61.44,0L61.44,0z M34.556,67.179c-1.313-1.188-1.415-3.216-0.226-4.529c1.188-1.313,3.216-1.415,4.529-0.227L52.3,74.611 l31.543-33.036c1.223-1.286,3.258-1.336,4.543-0.114c1.285,1.223,1.336,3.257,0.113,4.542L54.793,81.305l-0.004-0.004 c-1.195,1.257-3.182,1.338-4.475,0.168L34.556,67.179L34.556,67.179z M100.33,22.55C90.377,12.598,76.627,6.441,61.44,6.441 c-15.188,0-28.938,6.156-38.89,16.108c-9.953,9.953-16.108,23.702-16.108,38.89c0,15.188,6.156,28.938,16.108,38.891 c9.952,9.952,23.702,16.108,38.89,16.108c15.187,0,28.937-6.156,38.89-16.108c9.953-9.953,16.107-23.702,16.107-38.891 C116.438,46.252,110.283,32.502,100.33,22.55L100.33,22.55z" />
</Svg>
);
}
This will be rendered properly but if we try adding onClick
or onPress
to the <Svg>
component we will see that it doesn't work. We have to use some sort of touchable ReactNative component. And there are a few ones that are specifically made for this purpose. I decided to use <TouchableHighlight>
. So, my component became:
import { TouchableHighlight } from "react-native";
function Button({ onPress, size }) {
return (
<TouchableHighlight onPress={onPress}>
<Svg width={size} height={size} viewBox="0 0 122.88 122.88" {...lineProps}>
<Path d="..." />
</Svg>
</TouchableHighlight>
);
}
This works but has one big problem - the user needs to click on the exact SVG lines. The transparent bits of the icon are not clickable. To solve this problem we have to wrap our SVG into a View
component. Like so:
function Button({ onPress }) {
return (
<TouchableHighlight onPress={onPress}>
<View style={{ width: size, height: size }}>
<Svg width={size} height={size} viewBox="0 0 122.88 122.88" {...lineProps}>
<Path d="..." />
</Svg>
</View>
</TouchableHighlight>
);
}
With this, the icon is rendering and is clickable.
In my app I had to extend the code above to cover a few needs. Like for example having a default size and passing the color of the icon from the outside. Plus it is all TypeScript so here's the full component in case you need something similar:
import React from "react";
import { TouchableHighlight, View, GestureResponderEvent } from "react-native";
import Svg, { Path } from "react-native-svg";
const COLORS = {
green: "#86d98c",
};
const lineProps = {
strokeOpacity: 1,
strokeWidth: 1,
strokeLineCap: "round",
strokeLineJoin: "round",
};
export function Button({
color,
size,
onPress,
}: {
color: string;
size: number;
onPress: (e: GestureResponderEvent) => void;
}) {
return (
<TouchableHighlight onPress={onPress}>
<View style={{ width: size, height: size }}>
<Svg width={size} height={size} viewBox="0 0 122.88 122.88" fill={COLORS.green} {...lineProps} stroke={color}>
<Path d="..." />
</Svg>
</View>
</TouchableHighlight>
);
}
OK.defaultProps = {
size: 50,
color: COLORS.green,
onPress: () => {},
};