npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-3d-animated-buttons

v1.0.3

Published

A beautiful React Native animated button component with 3D press animation and haptic feedback

Readme

React Native 3D Animated Buttons

A beautiful React Native animated button component with Duolingo-style 3D press animation and haptic feedback. Perfect for creating engaging, interactive buttons that feel great to press. Written in TypeScript with full type definitions.

📱 Demo

🎬 Live Demo Video

Demo Video

📱 Screenshots

👆 Scan QR code with Expo Go to test on your device!

Features

  • Duolingo-style 3D press animation - Smooth button press effect with shadow that feels like the popular language learning app
  • 🎯 Spring animations - Natural, responsive press animations with configurable physics
  • 📳 Haptic feedback - Tactile response on button press (Light/Medium/Heavy)
  • 🎨 Customizable design - Colors, fonts, and styles
  • 🔄 Loading states - Built-in loading indicator with optional text
  • 🎯 Icon support - Built-in icons + comprehensive SVG icon library
  • ↔️ Icon positioning - Left or right icon placement for better UX
  • 🌍 RTL language support - Automatic right-to-left language detection
  • 📱 Responsive design - Adapts to different screen sizes and orientations
  • 🎪 Button types - Normal and capsule shapes
  • Enhanced accessibility - Labels, hints, hitSlop, and proper states
  • 🔒 Press lock mechanism - Prevents accidental double-presses
  • 📘 TypeScript - Full type definitions included
  • 🎨 50+ SVG Icons - Social, payment, arrows, actions, common UI icons

Installation

Install the package along with its required dependencies:

npm install react-native-3d-animated-buttons react-native-svg expo-haptics

That's it! Everything you need in one command to create Duolingo-style interactive buttons.

🚀 Quick Start

import { AnimatedButton } from 'react-native-3d-animated-buttons';

<AnimatedButton
  title="Continue"
  onPress={() => {}}
  icon="apple"
  type="capsule"
  backgroundColor="#20B2AA"
  shadowColor="#1A9B94"
/>

Usage

Basic Usage

import React from 'react';
import { View } from 'react-native';
import { AnimatedButton } from 'react-native-3d-animated-buttons';

const App = () => {
  return (
    <View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
      <AnimatedButton
        title="Press Me"
        onPress={() => console.log('Button pressed!')}
      />
    </View>
  );
};

export default App;

Advanced Usage

import { AnimatedButton } from 'react-native-3d-animated-buttons';

<AnimatedButton
  title="Sign in with Apple"
  onPress={() => handleAppleSignIn()}
  backgroundColor="#000000"
  shadowColor="#333333"
  textColor="#FFFFFF"
  hapticStyle="Medium"
  icon="apple"
  type="capsule"
  loading={isLoading}
  loadingText="Signing in..."
  disabled={false}
  fullWidth={true}
  minHeight={60}
  testID="apple-signin-button"
  accessibilityLabel="Sign in with Apple account"
  accessibilityHint="Double tap to sign in with your Apple ID"
  onLongPress={() => showHelp()}
  hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
  style={{ marginBottom: 20 }}
  textStyle={{ fontSize: 16, fontWeight: 'bold' }}
/>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | title | string | Required | Button text | | onPress | function | Required | Press handler function | | backgroundColor | ColorValue | '#20B2AA' | Main button background color | | shadowColor | ColorValue | '#1A9B94' | Shadow layer background color | | textColor | ColorValue | '#FFFFFF' | Button text color | | style | StyleProp | {} | Additional styles for button container | | textStyle | StyleProp | {} | Additional styles for button text | | disabled | boolean | false | Whether button is disabled | | hapticStyle | HapticStyle | 'Heavy' | Haptic feedback style: 'Light', 'Medium', 'Heavy' | | icon | IconType | null | Built-in icon type: 'apple', 'google', 'phone', 'facebook', or null | | customIcon | IconComponent | null | Custom SVG icon component (highest priority) | | customIconSvg | string | null | Custom SVG icon as string markup | | iconSize | number | 24 | Icon size (width and height in pixels) | | iconPosition | IconPosition | 'left' | Icon position: 'left' or 'right' (auto-swaps for RTL) | | loading | boolean | false | Whether to show loading indicator | | loadingText | string | null | Text to show when loading (optional) | | type | ButtonType | 'normal' | Button type: 'normal' or 'capsule' | | fullWidth | boolean | true | Whether button should take full width | | minHeight | number | 52 | Minimum height of the button | | testID | string | undefined | Test identifier for testing frameworks | | accessibilityLabel | string | title | Accessibility label for screen readers | | accessibilityHint | string | undefined | Accessibility hint (auto-sets to 'In progress' when loading) | | onLongPress | function | undefined | Long press handler function | | hitSlop | Insets | undefined | Touch target expansion for better accessibility | | disableAnimations | boolean | false | Disable animations for accessibility (reduced motion) |

Examples

Different Button Types

// Normal button
<AnimatedButton
  title="Normal Button"
  onPress={() => {}}
  type="normal"
/>

// Capsule button
<AnimatedButton
  title="Capsule Button"
  onPress={() => {}}
  type="capsule"
/>

With Built-in Icons

// Apple Sign In
<AnimatedButton
  title="Sign in with Apple"
  onPress={() => {}}
  icon="apple"
  backgroundColor="#000000"
  shadowColor="#333333"
/>

// Google Sign In
<AnimatedButton
  title="Sign in with Google"
  onPress={() => {}}
  icon="google"
  backgroundColor="#FFFFFF"
  shadowColor="#E0E0E0"
  textColor="#1A1A1A"
/>

// Facebook Sign In
<AnimatedButton
  title="Continue with Facebook"
  onPress={() => {}}
  icon="facebook"
  backgroundColor="#1877F2"
  shadowColor="#1464C7"
/>

// Phone Sign In
<AnimatedButton
  title="Sign in with Phone"
  onPress={() => {}}
  icon="phone"
  backgroundColor="#34C759"
  shadowColor="#2AA946"
/>

With Custom Icons

// Method 1: Custom React Component (Recommended)
import MyCustomIcon from './icons/MyCustomIcon';

<AnimatedButton
  title="Custom Icon Button"
  onPress={() => {}}
  customIcon={MyCustomIcon}
  iconSize={24}
  backgroundColor="#FF6B9D"
  shadowColor="#E55A87"
/>

// Method 2: SVG String
<AnimatedButton
  title="SVG String Icon"
  onPress={() => {}}
  customIconSvg={`<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
    <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" fill="currentColor"/>
  </svg>`}
  iconSize={20}
  backgroundColor="#FFD700"
  shadowColor="#E6C200"
/>

// Method 3: Icon Priority (customIcon > customIconSvg > icon)
<AnimatedButton
  title="Priority Example"
  onPress={() => {}}
  icon="apple"           // Ignored (lowest priority)
  customIcon={HeartIcon} // Used (highest priority)
/>

// Method 4: Icon Positioning for Better UX
<AnimatedButton
  title="Previous"
  onPress={() => {}}
  customIconSvg={arrowLeftSvg}
  iconPosition="left"    // Arrow on left (default)
/>

<AnimatedButton
  title="Continue"
  onPress={() => {}}
  customIconSvg={arrowRightSvg}
  iconPosition="right"   // Arrow on right (better UX!)
/>

Loading States

<AnimatedButton
  title="Submit"
  onPress={() => {}}
  loading={isSubmitting}
  loadingText="Submitting..."
  disabled={isSubmitting}
/>

Custom Colors and Styles

<AnimatedButton
  title="Custom Button"
  onPress={() => {}}
  backgroundColor="#FF6B6B"
  shadowColor="#E55555"
  textColor="#FFFFFF"
  style={{ 
    marginHorizontal: 20,
    marginBottom: 10 
  }}
  textStyle={{ 
    fontSize: 18,
    fontWeight: '600' 
  }}
/>

Font Customization

<AnimatedButton
  title="Custom Font Button"
  onPress={() => {}}
  backgroundColor="#FF6B35"
  shadowColor="#E55529"
  textStyle={{
    fontFamily: Platform.OS === 'ios' ? 'YourCustomFont-Bold' : 'your_custom_font_bold',
    fontSize: 18,
    fontWeight: '600',
    letterSpacing: 0.5,
  }}
/>

<AnimatedButton
  title="System Font"
  onPress={() => {}}
  textStyle={{
    fontFamily: Platform.OS === 'ios' ? 'System' : 'Roboto',
    fontSize: 16,
    fontWeight: '500',
  }}
/>

Using the Icon Library

// Import icons from the comprehensive library
import { AnimatedButton, icons } from 'react-native-3d-animated-buttons';

// Social media icons
<AnimatedButton
  title="Sign in with Apple"
  onPress={() => {}}
  customIconSvg={icons.appleV1}
  iconSize={20}
  backgroundColor="#000000"
/>

// Payment icons
<AnimatedButton
  title="Add to Cart"
  onPress={() => {}}
  customIconSvg={icons.cartV1}
  iconSize={18}
  backgroundColor="#FF6B35"
/>

// Arrow icons with positioning
<AnimatedButton
  title="Continue"
  onPress={() => {}}
  customIconSvg={icons.arrowRightV1}
  iconPosition="right"
  backgroundColor="#34C759"
/>

// Common UI icons
<AnimatedButton
  title="Like Post"
  onPress={() => {}}
  customIconSvg={icons.heartV1}
  iconSize={16}
  backgroundColor="#FF6B9D"
/>

Advanced Features

Spring Animations

The component uses spring-based animations for a natural, responsive feel that mimics the satisfying button press experience found in apps like Duolingo:

// Custom spring configuration (built-in)
<AnimatedButton
  title="Spring Button"
  onPress={() => {}}
  // Uses: stiffness: 300, damping: 20, mass: 0.4
/>

RTL Language Support

Automatically detects and adapts to right-to-left languages:

// Icon automatically swaps to right side in RTL languages
<AnimatedButton
  title="Continue"
  icon="arrow-right"
  // iconPosition not specified - auto-detects RTL
/>

Accessibility Features

Enhanced accessibility with proper labels, hints, and touch targets:

<AnimatedButton
  title="Accessible Button"
  onPress={() => {}}
  accessibilityLabel="Primary action button"
  accessibilityHint="Double tap to perform the main action"
  hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
  testID="primary-button"
/>

// Reduced motion support for accessibility
<AnimatedButton
  title="Static Button"
  onPress={() => {}}
  disableAnimations={true} // No animations for users who prefer reduced motion
/>

Press Lock Mechanism

Prevents accidental double-presses with configurable timing:

<AnimatedButton
  title="Protected Button"
  onPress={() => handleExpensiveOperation()}
  // Automatically prevents rapid double-presses
/>

Reduced Motion Support

Respects user accessibility preferences by allowing animation disabling:

<AnimatedButton
  title="Accessible Button"
  onPress={() => {}}
  disableAnimations={true} // Respects user's reduced motion preference
/>

Haptic Feedback

The component uses Expo Haptics for tactile feedback. You can customize the intensity:

  • 'Light' - Subtle feedback
  • 'Medium' - Moderate feedback
  • 'Heavy' - Strong feedback (default)
<AnimatedButton
  title="Light Haptic"
  onPress={() => {}}
  hapticStyle="Light"
/>

TypeScript Support

This package is written in TypeScript and includes full type definitions. You'll get excellent IntelliSense support and type safety:

import { AnimatedButton, AnimatedButtonProps, IconType, HapticStyle, ButtonType, IconPosition } from 'react-native-3d-animated-buttons';

// All props are fully typed
const MyButton: React.FC = () => {
  const handlePress = (): void => {
    console.log('Button pressed!');
  };

  return (
    <AnimatedButton
      title="Typed Button"
      onPress={handlePress}
      hapticStyle="Medium" // Autocomplete available: 'Light' | 'Medium' | 'Heavy'
      icon="apple" // Autocomplete available: 'apple' | 'google' | 'phone' | 'facebook'
      type="capsule" // Autocomplete available: 'normal' | 'capsule'
      iconPosition="right" // Autocomplete available: 'left' | 'right'
    />
  );
};

Test App

This package includes a comprehensive test app demonstrating all features:

cd test-app
npm install
npx expo start

The test app showcases:

  • All button features - Basic, social, icons, types, loading
  • Icon positioning - Left/right placement examples
  • Font customization - Typography styling options
  • Side-by-side layouts - Responsive button pairing
  • Professional architecture - Clean, modular code structure

Perfect for understanding how to integrate the component in your project!

Responsive Design

The component automatically adapts to different screen sizes and orientations using a responsive scaling system based on device dimensions. It responds to orientation changes in real-time for optimal user experience, ensuring your Duolingo-style buttons look great on any device.

Performance Features

  • React.memo - Prevents unnecessary re-renders
  • useCallback - Stable event handler references
  • useMemo - Optimized computed values
  • useWindowDimensions - Efficient orientation change handling
  • Press lock - Prevents rapid double-presses
  • Duolingo-inspired UX - Smooth, satisfying button interactions that users love

License

MIT

Contributing

Pull requests are welcome! Please make sure to update tests as appropriate.

Support

If you like this component, please give it a ⭐ on GitHub!