Caracal Starter
UI & Theming

HeroUI Native Components

Guide to using HeroUI Native as the primary UI component library in Caracal starter.

Caracal uses HeroUI Native (v1.0.5) as the primary UI component library, providing 30+ production-ready components with built-in accessibility, theming, and styling.

Why HeroUI Native?

HeroUI Native is a comprehensive UI component library designed specifically for React Native, offering:

  • Production-ready components with consistent design
  • Built-in accessibility with proper ARIA labels and keyboard navigation
  • Theme integration with automatic dark mode support
  • TypeScript support with full type safety
  • Native performance optimized for React Native
  • Uniwind integration for TailwindCSS-like styling

Always refer to the official documentation for the latest updates.

Available Components

HeroUI Native provides 30+ components. Here are the most commonly used:

Form Components

  • TextField - Text input with label, error states, and variants
  • Button - Pressable button with multiple variants (solid, outline, ghost, light, flat)
  • Checkbox - Checkbox with label and controlled state support
  • Switch - Toggle switch component with theming
  • RadioGroup - Radio button groups with single selection
  • Select - Dropdown select with native modal support
  • FormField - Complete form field wrapper with validation

Layout Components

  • Card - Content container with header, body, footer sections
  • Tabs - Tabbed navigation with customizable styles
  • Accordion - Collapsible content sections
  • Divider - Visual separator with orientation support
  • Surface - Container with elevation and shadow effects
  • ScrollShadow - Scroll container with shadow indicators

Feedback Components

  • Dialog - Modal dialogs with customizable actions
  • Toast - Notification messages with auto-dismiss
  • Popover - Contextual popovers attached to elements
  • BottomSheet - Bottom sheet modals for mobile UX
  • Spinner - Loading spinners with various sizes
  • Skeleton - Loading placeholders for content

Display Components

  • Avatar - User avatars with image, initials, or icon
  • Chip - Tags and labels with removable support
  • ErrorView - Error state display with retry actions
  • PressableFeedback - Pressable wrapper with visual feedback

Basic Usage

Import components from heroui-native:

import { Button, TextField, Card } from 'heroui-native';
import { View } from '@/components/ui';

export function MyScreen() {
  return (
    <View className="flex-1 p-4">
      <Card>
        <Card.Header>
          <Text className="text-xl font-bold">Login</Text>
        </Card.Header>
        <Card.Body>
          <TextField label="Email" placeholder="Enter your email" />
          <TextField label="Password" secureTextEntry />
        </Card.Body>
        <Card.Footer>
          <Button variant="solid" color="primary">
            Sign In
          </Button>
        </Card.Footer>
      </Card>
    </View>
  );
}

Component Variants

Most HeroUI Native components support multiple variants:

Button Variants

import { Button } from 'heroui-native';

<Button variant="solid">Solid Button</Button>
<Button variant="outline">Outline Button</Button>
<Button variant="ghost">Ghost Button</Button>
<Button variant="light">Light Button</Button>
<Button variant="flat">Flat Button</Button>

TextField Variants

import { TextField } from 'heroui-native';

<TextField variant="outline" label="Outline" />
<TextField variant="underline" label="Underline" />
<TextField variant="filled" label="Filled" />

Card Variants

import { Card } from 'heroui-native';

<Card variant="elevated">Elevated Card</Card>
<Card variant="outlined">Outlined Card</Card>
<Card variant="filled">Filled Card</Card>

Theming and Styling

Using with Uniwind

HeroUI Native components work seamlessly with Uniwind (TailwindCSS for React Native):

import { Button } from 'heroui-native';
import { View } from '@/components/ui';

<View className="flex-row gap-2 p-4">
  <Button className="flex-1">Button 1</Button>
  <Button className="flex-1">Button 2</Button>
</View>

Color Customization

Components support color props:

<Button color="primary">Primary</Button>
<Button color="secondary">Secondary</Button>
<Button color="success">Success</Button>
<Button color="warning">Warning</Button>
<Button color="danger">Danger</Button>

Size Variants

<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>

Form Handling

HeroUI Native components integrate with react-hook-form through controlled wrappers:

import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import * as z from 'zod';
import { Button } from 'heroui-native';
import { ControlledInput } from '@/components/ui';

const schema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(6, 'Password too short'),
});

type FormData = z.infer<typeof schema>;

export function LoginForm() {
  const { control, handleSubmit } = useForm<FormData>({
    resolver: zodResolver(schema),
  });

  return (
    <>
      <ControlledInput
        control={control}
        name="email"
        label="Email"
      />
      <ControlledInput
        control={control}
        name="password"
        label="Password"
        secureTextEntry
      />
      <Button onPress={handleSubmit(onSubmit)}>
        Login
      </Button>
    </>
  );
}

See Forms documentation for complete form handling patterns.

Dark Mode Support

HeroUI Native components automatically adapt to the app's theme:

import { Button } from 'heroui-native';

export function ThemedButton() {
  // Components automatically use theme colors
  return (
    <Button variant="solid" color="primary">
      Theme-aware Button
    </Button>
  );
}

The theme is managed by the AppThemeProvider in src/lib/contexts/theme-provider.tsx.

Component Showcases

The Caracal starter includes 30 interactive component showcases in the (components) route group. These showcases demonstrate every HeroUI Native component with:

  • All available variants and props
  • Usage examples and code snippets
  • Dark mode demonstrations
  • Form integration patterns
  • Accessibility features

Accessing Showcases

Run the app and navigate to the Components section:

pnpm start

Available Showcases

Form Components (8):

  1. TextField - src/app/(components)/text-field.tsx
  2. Button - src/app/(components)/button.tsx
  3. Checkbox - src/app/(components)/checkbox.tsx
  4. Switch - src/app/(components)/switch.tsx
  5. RadioGroup - src/app/(components)/radio-group.tsx
  6. Select - src/app/(components)/select.tsx
  7. SelectNativeModal - src/app/(components)/select-native-modal.tsx
  8. FormField - src/app/(components)/form-field.tsx

Layout Components (7): 9. Card - src/app/(components)/card.tsx 10. Tabs - src/app/(components)/tabs.tsx 11. Accordion - src/app/(components)/accordion.tsx 12. Divider - src/app/(components)/divider.tsx 13. Surface - src/app/(components)/surface.tsx 14. ScrollShadow - src/app/(components)/scroll-shadow.tsx 15. PressableFeedback - src/app/(components)/pressable-feedback.tsx

Feedback Components (9): 16. Dialog - src/app/(components)/dialog.tsx 17. DialogNativeModal - src/app/(components)/dialog-native-modal.tsx 18. Toast - src/app/(components)/toast.tsx 19. ToastNativeModal - src/app/(components)/toast-native-modal.tsx 20. Popover - src/app/(components)/popover.tsx 21. PopoverNativeModal - src/app/(components)/popover-native-modal.tsx 22. BottomSheet - src/app/(components)/bottom-sheet.tsx 23. BottomSheetNativeModal - src/app/(components)/bottom-sheet-native-modal.tsx 24. Spinner - src/app/(components)/spinner.tsx 25. Skeleton - src/app/(components)/skeleton.tsx

Other Components (5): 26. Avatar - src/app/(components)/avatar.tsx 27. Chip - src/app/(components)/chip.tsx 28. ErrorView - src/app/(components)/error-view.tsx 29. ComponentList - src/app/(components)/component-list.tsx (navigation)

See Component Showcase Guide for detailed documentation on using showcases as learning resources.

Common Patterns

Loading States

import { useState } from 'react';
import { Button } from 'heroui-native';

export function AsyncButton() {
  const [loading, setLoading] = useState(false);

  return (
    <Button
      isLoading={loading}
      onPress={async () => {
        setLoading(true);
        await doSomething();
        setLoading(false);
      }}
    >
      Submit
    </Button>
  );
}

Disabled States

<Button isDisabled>Disabled Button</Button>
<TextField isDisabled label="Disabled Input" />
<Checkbox isDisabled>Disabled Checkbox</Checkbox>

Icon Integration

import { Button } from 'heroui-native';
import { IconCheck } from '@/components/ui/icons';

<Button startContent={<IconCheck size={20} />}>
  Save
</Button>

Responsive Sizing

import { useWindowDimensions } from 'react-native';
import { Button } from 'heroui-native';

export function ResponsiveButton() {
  const { width } = useWindowDimensions();
  const size = width < 375 ? 'sm' : 'md';

  return <Button size={size}>Responsive</Button>;
}

Best Practices

DO

  • Use HeroUI Native components for all UI elements (buttons, inputs, cards, etc.)
  • Leverage component variants instead of custom styling
  • Use the className prop for layout and spacing with Uniwind
  • Refer to component showcases for usage examples
  • Check official docs for latest props and features

DON'T

  • Don't recreate HeroUI Native components from scratch
  • Don't override internal component styles unnecessarily
  • Don't use web-only HeroUI components (this is React Native)
  • Don't assume component APIs — check documentation
  • Don't ignore accessibility props (label, accessibilityLabel)

Migration from Custom Components

If you're migrating from older versions of the starter that had custom Button, Checkbox, or Select components:

Before (custom components):

import { Button, Checkbox, Select } from '@/components/ui';

<Button label="Click me" variant="primary" />
<Checkbox checked={checked} onChange={setChecked} label="Accept" />
<Select options={options} value={value} onSelect={setValue} />

After (HeroUI Native):

import { Button, Checkbox, Select } from 'heroui-native';

<Button variant="solid" color="primary">Click me</Button>
<Checkbox isSelected={checked} onChange={setChecked}>Accept</Checkbox>
<Select items={options} selectedKey={value} onSelectionChange={setValue} />

Documentation Resources

Troubleshooting

Component not rendering

Check that you've imported from the correct package:

// Correct
import { Button } from 'heroui-native';

// Wrong — these don't exist
import { Button } from '@/components/ui';
import { Button } from 'react-native';

TypeScript errors

Ensure you're using the correct prop names. HeroUI Native uses:

  • isDisabled not disabled
  • isLoading not loading
  • isSelected not checked (for Checkbox)
  • onPress not onClick

Styling issues

Animated.View from react-native-reanimated doesn't support className. Use the style prop instead:

import Animated from 'react-native-reanimated';

// Won't work
<Animated.View className="flex-1" />

// Correct
<Animated.View style={{ flex: 1 }} />

Version Information

  • HeroUI Native: 1.0.5
  • React Native Compatibility: 0.86.2+
  • React Version: 19+
  • Expo SDK: 57
  • Uniwind Version: 1.10.0

Next Steps:

On this page