ExpoStartup

CustomCard

A highly customizable card component with support for background images, shadows, borders, and more.

CustomCard

The CustomCard component provides a versatile container with various customization options for creating cards with different styling. It supports background images, shadows, borders, and interactive behavior.

Features

  • Multiple Rounded Styles: Choose from various border radius options
  • Customizable Padding: Control internal spacing
  • Shadow Options: Apply different shadow levels with platform-specific handling
  • Border Support: Optional borders with customizable color
  • Background Image: Support for image backgrounds with overlay options
  • Navigation Support: Built-in href prop for navigation
  • Horizontal Layout: Optional horizontal orientation
  • Theming Support: Automatically adapts to light/dark mode

Import

import CustomCard from '@/components/CustomCard';

Props

PropTypeDefaultDescription
rounded'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full''lg'Border radius of the card
padding'none' | 'sm' | 'md' | 'lg' | 'xl''md'Internal padding of the card
shadow'none' | 'sm' | 'md' | 'lg' | 'xl''none'Shadow intensity
borderbooleanfalseWhether to show a border
borderColorstringCustom border color (if not set, uses theme-based color)
backgroundbooleantrueWhether to show background color
elevationbooleantrueWhether to apply elevation (shadow on Android)
classNamestring''Additional Tailwind classes
styleViewStyleAdditional style object
backgroundImagestringURL for background image
backgroundImageStyleViewStyleStyle for background image
overlayColorstring'black'Color of overlay on background image
overlayOpacitynumber0.3Opacity of overlay (0-1)
horizontalbooleanfalseWhether to layout children horizontally
onPress() => voidFunction to call when card is pressed
hrefstringNavigation link for the card
childrenReactNodeRequiredContent to display inside the card

Usage Examples

Basic Card

<CustomCard>
  <Text className="font-bold text-lg">Simple Card</Text>
  <Text>This is a basic card with default styling.</Text>
</CustomCard>

Styled Card

<CustomCard
  rounded="xl"
  padding="lg"
  shadow="md"
  border={true}
>
  <Text className="font-bold text-lg">Styled Card</Text>
  <Text>This card has custom rounded corners, padding, shadow, and border.</Text>
</CustomCard>

Card with Background Image

<CustomCard
  backgroundImage="https://example.com/image.jpg"
  overlayOpacity={0.5}
  padding="lg"
>
  <Text className="font-bold text-lg text-white">Image Card</Text>
  <Text className="text-white">This card has a background image with overlay.</Text>
</CustomCard>

Interactive Card

<CustomCard
  onPress={() => alert('Card pressed!')}
  shadow="sm"
  padding="md"
>
  <Text className="font-bold text-lg">Clickable Card</Text>
  <Text>Tap this card to trigger an action.</Text>
</CustomCard>
<CustomCard
  href="/details/123"
  shadow="sm"
  padding="md"
>
  <Text className="font-bold text-lg">Navigation Card</Text>
  <Text>Tap to navigate to details.</Text>
</CustomCard>

Horizontal Card

<CustomCard
  horizontal={true}
  padding="md"
>
  <Image 
    source={{ uri: "https://example.com/thumbnail.jpg" }}
    className="w-20 h-20 rounded-lg mr-4"
  />
  <View>
    <Text className="font-bold text-lg">Horizontal Card</Text>
    <Text>This card displays content in a horizontal layout.</Text>
  </View>
</CustomCard>

Card Showcase

Profile Card

<CustomCard
  padding="lg"
  shadow="md"
  rounded="xl"
>
  <View className="items-center mb-4">
    <Image
      source={{ uri: "https://example.com/avatar.jpg" }}
      className="w-24 h-24 rounded-full mb-2"
    />
    <Text className="font-bold text-xl">John Doe</Text>
    <Text className="text-gray-500">Product Designer</Text>
  </View>
  
  <View className="flex-row justify-between mb-4">
    <View className="items-center">
      <Text className="font-bold">528</Text>
      <Text className="text-sm text-gray-500">Posts</Text>
    </View>
    <View className="items-center">
      <Text className="font-bold">12.4k</Text>
      <Text className="text-sm text-gray-500">Followers</Text>
    </View>
    <View className="items-center">
      <Text className="font-bold">489</Text>
      <Text className="text-sm text-gray-500">Following</Text>
    </View>
  </View>
  
  <Button title="Follow" />
</CustomCard>

Product Card

<CustomCard
  shadow="sm"
  padding="none"
  rounded="lg"
  href="/product/456"
>
  <Image
    source={{ uri: "https://example.com/product.jpg" }}
    className="w-full h-40 rounded-t-lg"
  />
  <View className="p-4">
    <Text className="text-sm text-blue-500 mb-1">Electronics</Text>
    <Text className="font-bold text-lg mb-1">Wireless Headphones</Text>
    <Text className="mb-2">High-quality sound with noise cancellation</Text>
    <View className="flex-row justify-between items-center">
      <Text className="font-bold text-lg">$129.99</Text>
      <Button title="Add to Cart" size="sm" />
    </View>
  </View>
</CustomCard>

Featured Article Card

<CustomCard
  backgroundImage="https://example.com/article-bg.jpg"
  padding="lg"
  rounded="xl"
  href="/article/789"
>
  <View className="min-h-[150px] justify-end">
    <Text className="text-white text-sm mb-2">FEATURED</Text>
    <Text className="text-white font-bold text-xl mb-1">
      The Future of Mobile Development
    </Text>
    <Text className="text-white opacity-80 mb-4">
      Exploring new trends and technologies
    </Text>
    <View className="flex-row items-center">
      <Image
        source={{ uri: "https://example.com/author.jpg" }}
        className="w-8 h-8 rounded-full mr-2"
      />
      <Text className="text-white">Jane Smith • 5 min read</Text>
    </View>
  </View>
</CustomCard>

Settings Card

<CustomCard
  padding="md"
  border={true}
  shadow="none"
  rounded="lg"
>
  <View className="flex-row items-center">
    <View className="bg-blue-100 dark:bg-blue-900 p-3 rounded-full mr-4">
      <Icon name="Lock" size={24} color="#3b82f6" />
    </View>
    <View className="flex-1">
      <Text className="font-bold text-lg">Privacy Settings</Text>
      <Text className="text-gray-500">Manage your privacy preferences</Text>
    </View>
    <Icon name="ChevronRight" size={20} />
  </View>
</CustomCard>

Card Variants

Shadow Variants

<View className="space-y-4">
  <CustomCard shadow="none" padding="md">
    <Text className="font-bold">No Shadow</Text>
    <Text>Default card with no shadow</Text>
  </CustomCard>
  
  <CustomCard shadow="sm" padding="md">
    <Text className="font-bold">Small Shadow</Text>
    <Text>Card with small shadow</Text>
  </CustomCard>
  
  <CustomCard shadow="md" padding="md">
    <Text className="font-bold">Medium Shadow</Text>
    <Text>Card with medium shadow</Text>
  </CustomCard>
  
  <CustomCard shadow="lg" padding="md">
    <Text className="font-bold">Large Shadow</Text>
    <Text>Card with large shadow</Text>
  </CustomCard>
  
  <CustomCard shadow="xl" padding="md">
    <Text className="font-bold">Extra Large Shadow</Text>
    <Text>Card with extra large shadow</Text>
  </CustomCard>
</View>

Rounded Variants

<View className="space-y-4">
  <CustomCard rounded="none" padding="md">
    <Text className="font-bold">No Rounded Corners</Text>
    <Text>Card with square corners</Text>
  </CustomCard>
  
  <CustomCard rounded="sm" padding="md">
    <Text className="font-bold">Small Rounded Corners</Text>
    <Text>Card with slightly rounded corners</Text>
  </CustomCard>
  
  <CustomCard rounded="lg" padding="md">
    <Text className="font-bold">Large Rounded Corners</Text>
    <Text>Card with large rounded corners (default)</Text>
  </CustomCard>
  
  <CustomCard rounded="2xl" padding="md">
    <Text className="font-bold">2XL Rounded Corners</Text>
    <Text>Card with very rounded corners</Text>
  </CustomCard>
</View>

Best Practices

Consistent Card Styling

Use consistent card styling throughout your application:

// Define standard card configurations
const cardStyles = {
  default: {
    rounded: 'lg',
    padding: 'md',
    shadow: 'sm',
  },
  featured: {
    rounded: 'xl',
    padding: 'lg',
    shadow: 'md',
  },
  minimal: {
    rounded: 'md',
    padding: 'sm',
    border: true,
    shadow: 'none',
  }
};
 
// Use them consistently
<CustomCard {...cardStyles.default}>
  <Text>Default card style</Text>
</CustomCard>
 
<CustomCard {...cardStyles.featured}>
  <Text>Featured card style</Text>
</CustomCard>
 
<CustomCard {...cardStyles.minimal}>
  <Text>Minimal card style</Text>
</CustomCard>

Background Image Best Practices

When using background images, ensure text remains readable:

// Good: Dark overlay for light text
<CustomCard
  backgroundImage="https://example.com/bright-image.jpg"
  overlayColor="black"
  overlayOpacity={0.5}
  padding="lg"
>
  <Text className="text-white font-bold text-xl">Readable Title</Text>
  <Text className="text-white">This text is readable on the dark overlay.</Text>
</CustomCard>
 
// Good: Light overlay for dark text
<CustomCard
  backgroundImage="https://example.com/dark-image.jpg"
  overlayColor="white"
  overlayOpacity={0.7}
  padding="lg"
>
  <Text className="text-black font-bold text-xl">Readable Title</Text>
  <Text className="text-black">This text is readable on the light overlay.</Text>
</CustomCard>
 
// Avoid: Insufficient contrast
<CustomCard
  backgroundImage="https://example.com/busy-image.jpg"
  overlayOpacity={0.1} // Too low
  padding="lg"
>
  <Text className="text-white font-bold text-xl">Hard to Read</Text>
  <Text className="text-white">This text might be difficult to read.</Text>
</CustomCard>

Interactive Cards

Use visual cues to indicate interactivity:

// Good: Visual cue for interactive card
<CustomCard
  href="/details"
  shadow="sm"
  padding="md"
  className="border-l-4 border-l-blue-500"
>
  <View className="flex-row justify-between items-center">
    <Text className="font-bold">View Details</Text>
    <Icon name="ChevronRight" size={20} />
  </View>
</CustomCard>
 
// Good: Button-like card
<CustomCard
  onPress={handlePress}
  className="bg-blue-500"
  padding="md"
  rounded="full"
>
  <Text className="text-white text-center font-bold">Tap to Continue</Text>
</CustomCard>

Implementation Details

The CustomCard component is built using React Native's View and ImageBackground components. It uses Tailwind CSS classes for styling, with dynamic class generation based on the provided props.

For background images, the component uses React Native's ImageBackground with an optional overlay. The overlay is implemented using a View with absolute positioning and customizable color and opacity.

The component handles platform-specific shadow implementation, using shadow utilities for iOS and elevation for Android. This ensures consistent appearance across platforms.

For interactive cards, the component wraps the content in either a TouchableOpacity (for onPress) or a Link component from Expo Router (for href), making it easy to create clickable cards.

Notes

  • Shadow implementation differs between iOS and Android due to platform differences
  • When using background images, ensure they are properly loaded and cached for optimal performance
  • For navigation cards using the href prop, make sure the target route exists in your navigation structure
  • The component automatically handles light/dark mode through Tailwind's dark mode classes
  • On Android, shadow is implemented using elevation, which affects z-index ordering