Mobile
Button
Explore the Snyth registry of premium, high-performance UI blocks.
Preview
Installation
1. Install Dependencies
This component requires React Native Reanimated. Please follow the official installation guide to set up the library in your React Native or Expo project first.
Then, install the required animation peer:
npm install react-native-reanimatedyarn add react-native-reanimatedpnpm add react-native-reanimatedbun add react-native-reanimated2. Add Component
Using CLI
Use the Snyth CLI to automatically add the button component to your project.
npx snyth add buttonbunx snyth add buttonManual Installation
Create the file at components/file-path/button.tsx.
import React, { useCallback } from "react";
import {
StyleSheet,
useColorScheme,
Pressable,
Dimensions,
Platform,
} from "react-native";
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
runOnJS,
interpolate,
interpolateColor,
} from "react-native-reanimated";
const { width: SCREEN_WIDTH } = Dimensions.get("window");
const THEME = {
black: "hsl(0, 0%, 0%)",
white: "hsl(0, 0%, 100%)",
};
interface ButtonProps {
label: string;
onAction?: () => void;
size?: "small" | "medium" | "large";
}
const Button: React.FC<ButtonProps> = ({
label,
onAction,
size = "medium",
}) => {
const isDark = useColorScheme() === "dark";
const mainColor = isDark ? THEME.white : THEME.black;
const inverseColor = isDark ? THEME.black : THEME.white;
const interaction = useSharedValue(0);
const SIZES = {
small: { height: 40, width: 120, fontSize: 12 },
medium: { height: 50, width: 200, fontSize: 14 },
large: { height: 60, width: SCREEN_WIDTH * 0.85, fontSize: 16 },
};
const config = SIZES[size];
const handleAction = useCallback(() => {
console.log("Action Triggered");
if (onAction) onAction();
}, [onAction]);
const handlePressIn = () => {
interaction.value = withTiming(1, { duration: 200 });
};
const handlePressOut = () => {
if (interaction.value >= 0.9) {
runOnJS(handleAction)();
}
interaction.value = withTiming(0, { duration: 200 });
};
const rButtonStyle = useAnimatedStyle(() => {
return {
backgroundColor: interpolateColor(
interaction.value,
[0, 1],
["transparent", mainColor],
),
borderColor: mainColor,
borderRadius: interpolate(
interaction.value,
[0, 1],
[4, config.height / 2],
),
borderWidth: 2,
};
});
const rTextStyle = useAnimatedStyle(() => {
return {
color: interpolateColor(
interaction.value,
[0, 1],
[mainColor, inverseColor],
),
};
});
return (
<Animated.View
style={[
styles.container,
{
height: config.height,
width: config.width,
},
rButtonStyle,
]}
>
<Pressable
onPressIn={handlePressIn}
onPressOut={handlePressOut}
style={styles.pressable}
>
<Animated.Text
style={[styles.label, { fontSize: config.fontSize }, rTextStyle]}
>
{label}
</Animated.Text>
</Pressable>
</Animated.View>
);
};
const styles = StyleSheet.create({
container: {
alignSelf: "center",
marginVertical: 10,
overflow: "hidden",
},
pressable: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
label: {
fontWeight: "900",
textTransform: "uppercase",
letterSpacing: 1.5,
fontFamily:
Platform.OS === "ios"
? "HelveticaNeue-CondensedBold"
: "sans-serif-condensed",
},
});
export default Button;Usage
import Button from "@/components/file-path/button";
export default function Example() {
return (
<div className="flex gap-4">
<Button label="Press me" />
</div>
);
}