UI Components

Build beautiful interfaces with React Native primitives and NativeWind. Learn component usage, theming, dark mode support, and responsive design patterns.

UI Components

AppLighter uses React Native primitives with NativeWind styling - no third-party UI component library required. You own the code and can customize everything.

Why React Native Primitives?

FeatureBenefit
No dependenciesNo UI library lock-in, zero bundle overhead
NativeWindTailwind CSS styling with className
Full controlCustomize any component exactly as needed
Dark modeBuilt-in light/dark theme via CSS variables
Cross-platformWorks on iOS, Android, and Web

Available Components

Use React Native components with NativeWind className:

Layout

ComponentImportPurpose
Viewreact-nativeFlexible container
SafeAreaViewreact-native-safe-area-contextSafe area wrapper
ScrollViewreact-nativeScrollable container
KeyboardAvoidingViewreact-nativeKeyboard-aware wrapper

Typography

ComponentImportPurpose
Textreact-nativeAll text content

Forms

ComponentImportPurpose
TextInputreact-nativeText input fields
Pressablereact-nativePressable buttons
TouchableOpacityreact-nativeTouch feedback

Lists

ComponentImportPurpose
FlatListreact-nativePerformant lists
SectionListreact-nativeGrouped lists

Feedback

ComponentImportPurpose
ActivityIndicatorreact-nativeLoading spinner
RefreshControlreact-nativePull-to-refresh

Media

ComponentImportPurpose
Imagereact-native / expo-imageImage display
ImageBackgroundreact-nativeBackground images

Icons

ComponentImportPurpose
Various iconslucide-react-native100+ approved icons

Usage Example

import { View, Text, Pressable, FlatList } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import { CheckSquare, ChevronRight } from 'lucide-react-native'
 
export default function HomeScreen() {
  return (
    <SafeAreaView className="flex-1 bg-background" edges={['top']}>
      <View className="px-4 pt-4">
        <Text className="text-2xl font-bold text-foreground">Tasks</Text>
      </View>
      <FlatList
        data={tasks}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <Pressable className="bg-card mx-4 my-2 p-4 rounded-xl flex-row items-center">
            <View className="bg-primary/10 p-2 rounded-lg mr-3">
              <CheckSquare size={20} className="text-primary" />
            </View>
            <View className="flex-1">
              <Text className="text-foreground font-semibold">{item.title}</Text>
              <Text className="text-muted-foreground text-sm">{item.due_date}</Text>
            </View>
            <ChevronRight size={20} className="text-muted-foreground" />
          </Pressable>
        )}
      />
    </SafeAreaView>
  )
}

Custom Components

AppLighter includes a few reusable custom components:

components/
├── index.ts              # Barrel exports
├── ThemeProvider.tsx      # Theme context provider
├── ThemeToggle.tsx        # Light/dark mode toggle button
└── ThemedView.tsx        # Theme-aware View wrapper

Styling with NativeWind

All styling uses NativeWind's className prop:

// Container with theme-aware colors
<View className="bg-card rounded-xl p-4 border border-border">
  <Text className="text-foreground font-bold text-lg">
    Card Title
  </Text>
  <Text className="text-muted-foreground mt-1">
    Card description text
  </Text>
</View>
 
// Primary action button
<Pressable className="bg-primary rounded-xl py-3 px-6 items-center">
  <Text className="text-primary-foreground font-semibold">
    Get Started
  </Text>
</Pressable>
 
// Destructive action
<Pressable className="bg-destructive rounded-xl py-3 px-6 items-center">
  <Text className="text-white font-semibold">Delete</Text>
</Pressable>

Theme Variables

Use semantic theme variables from theme.ts for consistent styling:

VariableLightDarkUsage
backgroundlight purple tintdeep indigoPage background
foregrounddeep indigolight purplePrimary text
cardwhiteindigo-900Card backgrounds
primaryindigo-500indigo-400Primary actions
mutedslate-100indigo-700Secondary elements
muted-foregroundslate-500indigo-300Secondary text
bordergray-200indigo-700Borders
destructivered-500red-400Danger/delete
accentlight indigoindigo-500Accent highlights

Dark Mode

Components automatically adapt to dark mode through semantic color tokens. The ThemeProvider handles switching:

import { ThemeToggle } from '@/components/ThemeToggle'
 
function ScreenHeader() {
  return (
    <View className="flex-row items-center justify-between px-4 pt-4">
      <Text className="text-2xl font-bold text-foreground">Settings</Text>
      <ThemeToggle />
    </View>
  )
}

For programmatic theme access:

import { useTheme } from '../src/hooks'
 
function MyComponent() {
  const { isDark, colorScheme, toggleColorScheme } = useTheme()
 
  return (
    <View className="bg-background flex-1">
      <Text className="text-foreground">
        Current theme: {colorScheme}
      </Text>
    </View>
  )
}

Important Styling Rules

  1. Use className only - Never use StyleSheet.create()
  2. Use semantic colors - Prefer bg-primary over bg-indigo-500
  3. No hardcoded colors - Always use theme variables
  4. ScrollView styling - Use contentContainerStyle (NOT contentContainerClassName)
  5. Modal styling - Use inline style prop (className doesn't work inside Modals)
  6. Icons - Only use approved icons from lucide-react-native (100+ available)
  7. SafeAreaView - Always wrap screens with SafeAreaView from react-native-safe-area-context

Lucide Icons

Import icons from lucide-react-native. Over 100 icons are approved for use:

import { Home, CheckSquare, User, Settings, Bell,
  Heart, Star, Search, Plus, X, ChevronRight, ArrowLeft } from 'lucide-react-native'
 
// Usage with cssInterop for className support
import { cssInterop } from 'nativewind'
cssInterop(CheckSquare, { className: { target: 'style', nativeStyleToProp: { color: true } } })
 
<CheckSquare size={24} className="text-primary" />

Best Practices

  1. Use semantic colors - Prefer bg-primary over bg-indigo-500
  2. Keep it simple - Build with primitives, add complexity only when needed
  3. Accessibility first - Use accessibilityLabel and proper contrast
  4. Use useCallback - Wrap FlatList item renderers and handlers
  5. Use useMemo - For derived data in components

Resources

Next Steps