Back to Components
🌈

Rainbow Button

Medium

An eye-catching button component with an animated rainbow gradient border. Uses SVG for smooth stroke animations that travel around the button perimeter.

Buttonv1.0.0Updated 2026-02-04

Live Preview

Open in Snack

Installation

1

npm install react-native-svg

2

cd ios && pod install

3

Copy RainbowButton.tsx to components

4

Import and use

⚠️ Native Setup Required:

  • iOS: Pod install handles setup
  • Android: Auto-linked in RN 0.60+

Source Code

RainbowButton.tsx
import React, { useRef, useEffect } from 'react';
import { View, Animated, Easing, TouchableOpacity, ViewStyle } from 'react-native';
import Svg, { Rect, Defs, LinearGradient, Stop } from 'react-native-svg';

const AnimatedRect = Animated.createAnimatedComponent(Rect);

const DEFAULT_COLORS = {
    WhiteSmoke: '#F5F5F5',
    white: '#FFFFFF',
};

interface RainbowButtonProps {
    width: number;
    height: number;
    borderRadius?: number;
    borderWidth?: number;
    children?: React.ReactNode;
    style?: ViewStyle;
    onPress?: () => void;
    backgroundColor?: string;
    colors?: {
        start?: string;
        middle?: string;
        end?: string;
        bg?: string;
    }
}

const RainbowButton = ({
    width,
    height,
    borderRadius = 16,
    borderWidth = 2,
    children,
    style,
    onPress,
    backgroundColor
}: RainbowButtonProps) => {
    const COLORS = DEFAULT_COLORS;
    const anim = useRef(new Animated.Value(0)).current;

    // Calculates perimeter of the rounded rectangle
    const perimeter = 2 * (width + height - 2 * borderRadius) + 2 * Math.PI * borderRadius;
    const sweepLength = perimeter * 0.35; // Length of colored segment

    useEffect(() => {
        Animated.loop(
            Animated.timing(anim, {
                toValue: 1,
                duration: 3000,
                useNativeDriver: true,
                easing: Easing.linear,
            })
        ).start();
    }, []);

    return (
        <TouchableOpacity
            activeOpacity={0.8}
            onPress={onPress}
            style={[
                {
                    width,
                    height,
                    borderRadius,
                    justifyContent: 'center',
                    alignItems: 'center',
                },
                style,
            ]}
        >
            <View style={{
                position: 'absolute',
                width,
                height,
                borderRadius,
                overflow: 'hidden',
            }}>
                <Svg width={width} height={height}>
                    <Defs>
                        <LinearGradient id="rainbow" x1="0" y1="0" x2={width} y2={height} gradientUnits="userSpaceOnUse">
                            <Stop offset="0%" stopColor="#00C0FF" />
                            <Stop offset="33%" stopColor="#FFCF00" />
                            <Stop offset="66%" stopColor="#FC4F4F" />
                            <Stop offset="100%" stopColor="#00C0FF" />
                        </LinearGradient>
                    </Defs>
                    {/* Transparent border pathway */}
                    <Rect
                        x={borderWidth / 2}
                        y={borderWidth / 2}
                        width={width - borderWidth}
                        height={height - borderWidth}
                        rx={borderRadius - borderWidth / 2}
                        ry={borderRadius - borderWidth / 2}
                        stroke={COLORS.WhiteSmoke}
                        strokeWidth={borderWidth}
                        fill="none"
                    />
                    {/* Animated rainbow segment */}
                    <AnimatedRect
                        x={borderWidth / 2}
                        y={borderWidth / 2}
                        width={width - borderWidth}
                        height={height - borderWidth}
                        rx={borderRadius - borderWidth / 2}
                        ry={borderRadius - borderWidth / 2}
                        stroke="url(#rainbow)"
                        strokeWidth={borderWidth}
                        fill="none"
                        strokeDasharray={`${sweepLength},${perimeter - sweepLength}`}
                        strokeDashoffset={anim.interpolate({
                            inputRange: [0, 1],
                            outputRange: [0, -perimeter],
                        })}
                    />
                </Svg>
            </View>
            <View
                style={{
                    width: width - borderWidth * 2,
                    height: height - borderWidth * 2,
                    borderRadius: borderRadius - borderWidth,
                    backgroundColor: backgroundColor || COLORS.WhiteSmoke,
                    alignItems: 'center',
                    justifyContent: 'center',
                }}
            >
                {children}
            </View>
        </TouchableOpacity>
    );
};

export default RainbowButton;

Usage Examples

Basic Usage

Simple rainbow button

Example 1
<RainbowButton
  width={200}
  height={50}
  onPress={() => console.log('Pressed')}
>
  <Text>Premium Feature</Text>
</RainbowButton>

Props

PropTypeDefaultDescription
width*number-Width of the button
height*number-Height of the button
children*ReactNode-Button content
onPress() => void-Press handler
animatedbooleantrueEnable border animation

Features

  • Animated gradient border effect
  • Customizable gradient colors
  • Adjustable animation speed
  • Static or animated modes
  • Press feedback
  • Fully customizable size

Dependencies

Required:

react-native-svg