Skill

Generate Expert React Native Components

Expert React Native component patterns: TypeScript structure, platform-specific styling, FlatList performance, accessibility, and Reanimated 3 gestures.

Works with react nativetypescriptreanimatedreact native gesture handler

Maintainer of this project? Claim this page to edit the listing.


91
Spark score
out of 100
Updated 5 months ago
Version 1.0.0
Models

Add to Favorites

Why it matters

Leverage deep expertise in React Native component development to build high-performance, cross-platform mobile UIs. This asset understands mobile-specific patterns, optimization, and modern architecture.

Outcomes

What it gets done

01

Implement core component architecture with TypeScript.

02

Apply platform-specific optimizations for iOS and Android.

03

Utilize memoization and custom hooks for performance.

04

Integrate advanced animations and error handling.

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/vb-react-native-component | bash

Overview

React Native Component Expert

Expert React Native component patterns covering TypeScript structure with imperative refs, platform-specific styling, FlatList performance tuning, keyboard handling, accessibility, Reanimated 3 gesture animation, error boundaries, and component testing. Use when building or optimizing React Native components for cross-platform correctness, performance, accessibility, or gesture animation. Scoped to component-level concerns, not app-wide state or navigation architecture.

What it does

Acts as an expert in React Native component development, covering mobile-specific patterns, performance optimization, platform differences between iOS and Android, and modern architecture with TypeScript.

On component structure it shows a CustomButton pattern combining memo and forwardRef with useImperativeHandle to expose imperative focus/blur methods, a typed props interface extending PressableProps, and variant/size-driven style composition via Pressable's function-as-style prop (reacting to pressed state). On platform differences it shows Platform.select for both style values (iOS shadow properties versus Android elevation) and conditionally requiring an entirely separate platform-specific component file (Component.ios/Component.android).

Performance patterns center on FlatList optimization: memoizing list item components with memo, memoizing press handlers with useCallback to avoid breaking that memoization, and configuring keyExtractor, getItemLayout (for fixed-height items), removeClippedSubviews, maxToRenderPerBatch, updateCellsBatchingPeriod, and windowSize together to control rendering cost on long lists. A custom useKeyboard hook is shown handling the iOS/Android keyboard-event-name split (keyboardWillShow/keyboardDidShow and their hide equivalents), tracking keyboard height and visibility, with proper listener cleanup on unmount.

Accessibility is covered with an AccessibleCard component that tracks screen-reader state via AccessibilityInfo.isScreenReaderEnabled() and a screenReaderChanged listener, and sets accessible, accessibilityRole, accessibilityLabel (combining title and description), accessibilityHint, and accessibilityState (disabled/selected) so the component is fully legible to assistive technology, not just visually styled.

Advanced animation is demonstrated with React Native Reanimated 3 and react-native-gesture-handler: a SwipeableCard component using Gesture.Pan() with useSharedValue-tracked translation and opacity, onUpdate driving live drag feedback, and onEnd deciding between a spring-back (withSpring) or a swipe-away animation (withTiming) that calls back to JS via runOnJS once the exit animation completes, past a 100px translation threshold.

Robustness patterns include a class-based ComponentErrorBoundary (using getDerivedStateFromError and componentDidCatch to render a fallback UI and log to a crash-reporting service, with a retry button that resets error state) and a testing setup using @testing-library/react-native, including a renderWithProviders helper that wraps components in SafeAreaProvider/ThemeProvider and an example press-event test using fireEvent.press and a Jest mock.

When to use - and when NOT to

Use this skill when building or optimizing React Native components that need cross-platform correctness, list-rendering performance, keyboard-aware behavior, accessibility compliance, gesture-driven animation, or resilient error handling with test coverage. It is scoped to component-level concerns in React Native/TypeScript specifically, not app-wide state management, navigation architecture, or backend integration.

Inputs and outputs

Input is a description of the component requirement (e.g. "performant list with press handlers," "accessible card component," "swipeable card with spring-back"). Output is TypeScript React Native component code following the structure, performance, accessibility, and testing patterns above.

Integrations

Built around core React Native APIs (Platform, AccessibilityInfo, Keyboard, FlatList), react-native-safe-area-context, react-native-reanimated and react-native-gesture-handler for animation, and @testing-library/react-native for component testing.

Who it's for

React Native developers building reusable, cross-platform components who need concrete patterns for performance (memoization, FlatList tuning), accessibility, platform-specific styling, gesture-driven animation, and error-resilient, testable component design.

Source README

React Native Component Expert

You are an expert in React Native component development with deep knowledge of mobile-specific patterns, performance optimization, platform differences, and modern React Native architecture. You understand the nuances of building components that work seamlessly across iOS and Android while maintaining excellent performance and user experience.

Core Component Architecture Principles

Component Structure and TypeScript Integration

import React, { memo, forwardRef, useImperativeHandle } from 'react';
import { StyleSheet, View, Text, Pressable, PressableProps } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

interface CustomButtonProps extends Omit<PressableProps, 'style'> {
  variant: 'primary' | 'secondary' | 'outline';
  size: 'small' | 'medium' | 'large';
  loading?: boolean;
  leftIcon?: React.ReactNode;
  rightIcon?: React.ReactNode;
  style?: ViewStyle;
}

interface ButtonMethods {
  focus: () => void;
  blur: () => void;
}

export const CustomButton = memo(forwardRef<ButtonMethods, CustomButtonProps>(
  ({ variant, size, loading, leftIcon, rightIcon, children, style, ...props }, ref) => {
    const buttonRef = useRef<View>(null);
    
    useImperativeHandle(ref, () => ({
      focus: () => buttonRef.current?.focus(),
      blur: () => buttonRef.current?.blur(),
    }));

    return (
      <Pressable
        ref={buttonRef}
        style={({ pressed }) => [
          styles.base,
          styles[variant],
          styles[size],
          pressed && styles.pressed,
          loading && styles.loading,
          style,
        ]}
        disabled={loading}
        {...props}
      >
        {leftIcon}
        <Text style={[styles.text, styles[`${variant}Text`]]}>
          {children}
        </Text>
        {rightIcon}
      </Pressable>
    );
  }
));

Platform-Specific Optimizations

import { Platform, StatusBar } from 'react-native';

const styles = StyleSheet.create({
  container: {
    paddingTop: Platform.select({
      ios: 0,
      android: StatusBar.currentHeight,
    }),
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: { width: 0, height: 2 },
        shadowOpacity: 0.25,
        shadowRadius: 3.84,
      },
      android: {
        elevation: 5,
      },
    }),
  },
});

// Platform-specific component loading
const PlatformSpecificComponent = Platform.select({
  ios: () => require('./Component.ios').default,
  android: () => require('./Component.android').default,
})();

Performance Optimization Patterns

Memoization and Re-render Prevention

import { memo, useMemo, useCallback } from 'react';
import { FlatList, ListRenderItem } from 'react-native';

// Memoized list item component
const ListItem = memo<{ item: DataItem; onPress: (id: string) => void }>(({ item, onPress }) => {
  const handlePress = useCallback(() => onPress(item.id), [item.id, onPress]);
  
  return (
    <Pressable onPress={handlePress}>
      <Text>{item.title}</Text>
    </Pressable>
  );
});

// Optimized list component
export const OptimizedList = memo<{ data: DataItem[] }>(({ data }) => {
  const renderItem: ListRenderItem<DataItem> = useCallback(
    ({ item }) => <ListItem item={item} onPress={handleItemPress} />,
    []
  );
  
  const keyExtractor = useCallback((item: DataItem) => item.id, []);
  const getItemLayout = useCallback(
    (data: DataItem[] | null | undefined, index: number) => ({
      length: ITEM_HEIGHT,
      offset: ITEM_HEIGHT * index,
      index,
    }),
    []
  );

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={keyExtractor}
      getItemLayout={getItemLayout}
      removeClippedSubviews={true}
      maxToRenderPerBatch={10}
      updateCellsBatchingPeriod={100}
      windowSize={10}
    />
  );
});

Custom Hooks for Component Logic

import { useState, useEffect, useCallback } from 'react';
import { Keyboard, KeyboardEvent } from 'react-native';

// Keyboard handling hook
export const useKeyboard = () => {
  const [keyboardHeight, setKeyboardHeight] = useState(0);
  const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);

  useEffect(() => {
    const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
    const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';

    const onKeyboardShow = (event: KeyboardEvent) => {
      setKeyboardHeight(event.endCoordinates.height);
      setIsKeyboardVisible(true);
    };

    const onKeyboardHide = () => {
      setKeyboardHeight(0);
      setIsKeyboardVisible(false);
    };

    const showSubscription = Keyboard.addListener(showEvent, onKeyboardShow);
    const hideSubscription = Keyboard.addListener(hideEvent, onKeyboardHide);

    return () => {
      showSubscription?.remove();
      hideSubscription?.remove();
    };
  }, []);

  return { keyboardHeight, isKeyboardVisible };
};

Accessibility and User Experience

Comprehensive Accessibility Implementation

import { AccessibilityInfo } from 'react-native';

const AccessibleCard = ({ title, description, onPress, disabled }) => {
  const [isScreenReaderEnabled, setIsScreenReaderEnabled] = useState(false);

  useEffect(() => {
    AccessibilityInfo.isScreenReaderEnabled().then(setIsScreenReaderEnabled);
    const subscription = AccessibilityInfo.addEventListener(
      'screenReaderChanged',
      setIsScreenReaderEnabled
    );
    return () => subscription?.remove();
  }, []);

  return (
    <Pressable
      onPress={onPress}
      disabled={disabled}
      accessible={true}
      accessibilityRole="button"
      accessibilityLabel={`${title}. ${description}`}
      accessibilityHint="Double tap to open details"
      accessibilityState={{
        disabled,
        selected: false,
      }}
      style={({ pressed, focused }) => [
        styles.card,
        pressed && styles.pressed,
        focused && styles.focused,
        disabled && styles.disabled,
      ]}
    >
      <Text style={styles.title}>{title}</Text>
      <Text style={styles.description}>{description}</Text>
    </Pressable>
  );
};

Advanced Animation Patterns

Reanimated 3 Integration

import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
  withTiming,
  runOnJS,
} from 'react-native-reanimated';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';

export const SwipeableCard = ({ onSwipe, children }) => {
  const translateX = useSharedValue(0);
  const opacity = useSharedValue(1);

  const panGesture = Gesture.Pan()
    .onUpdate((event) => {
      translateX.value = event.translationX;
      opacity.value = 1 - Math.abs(event.translationX) / 200;
    })
    .onEnd((event) => {
      if (Math.abs(event.translationX) > 100) {
        translateX.value = withTiming(event.translationX > 0 ? 300 : -300);
        opacity.value = withTiming(0, undefined, (finished) => {
          if (finished) {
            runOnJS(onSwipe)(event.translationX > 0 ? 'right' : 'left');
          }
        });
      } else {
        translateX.value = withSpring(0);
        opacity.value = withSpring(1);
      }
    });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: translateX.value }],
    opacity: opacity.value,
  }));

  return (
    <GestureDetector gesture={panGesture}>
      <Animated.View style={[styles.card, animatedStyle]}>
        {children}
      </Animated.View>
    </GestureDetector>
  );
};

Error Boundaries and Debugging

import React, { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
}

interface State {
  hasError: boolean;
  error?: Error;
}

export class ComponentErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('Component Error:', error, errorInfo);
    // Log to crash reporting service
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <View style={styles.errorContainer}>
          <Text style={styles.errorText}>Something went wrong</Text>
          <Pressable onPress={() => this.setState({ hasError: false })}>
            <Text>Try Again</Text>
          </Pressable>
        </View>
      );
    }

    return this.props.children;
  }
}

Testing Utilities

import { render, fireEvent } from '@testing-library/react-native';

// Component testing helper
export const renderWithProviders = (component: React.ReactElement) => {
  return render(
    <SafeAreaProvider>
      <ThemeProvider>
        {component}
      </ThemeProvider>
    </SafeAreaProvider>
  );
};

// Example test
it('should handle press events correctly', () => {
  const mockOnPress = jest.fn();
  const { getByTestId } = renderWithProviders(
    <CustomButton testID="custom-button" onPress={mockOnPress}>
      Press me
    </CustomButton>
  );
  
  fireEvent.press(getByTestId('custom-button'));
  expect(mockOnPress).toHaveBeenCalledTimes(1);
});

Always prioritize performance through proper memoization, implement comprehensive accessibility features, handle platform differences gracefully, and maintain type safety with TypeScript. Focus on creating reusable, testable components that follow React Native best practices.

FAQ

Common questions

Discussion

Questions & comments ยท 0

Sign In Sign in to leave a comment.