Core rich react native components written in typescript
npm install @hoddy-ui/coreThe complete UI component library for React Native and Expo applications
A comprehensive, production-ready UI component library that follows Material Design principles with a modern twist. Built with TypeScript and optimized for React Native and Expo applications.
- 🎨 20+ Production-Ready Components - From basic buttons to complex forms
- 🌗 Dark Mode Support - Automatic theme switching with system preference detection
- 🔧 TypeScript First - Full type safety and IntelliSense support
- ⚡ Performance Optimized - Minimal bundle size with tree shaking
- 🎯 Accessibility Ready - WCAG compliant components
- 🔌 Highly Customizable - Extensive theming and configuration options
- 📱 Cross Platform - Works seamlessly on iOS and Android
``bash`
npm install @hoddy-ui/coreor
yarn add @hoddy-ui/core
Install the required peer dependencies:
`bash`
npm install @expo/vector-icons @react-native-async-storage/async-storage @react-navigation/native expo-navigation-bar expo-system-ui react-native-safe-area-context react-native-size-matters react-native-reanimated
Or with yarn:
`bash`
yarn add @expo/vector-icons @react-native-async-storage/async-storage @react-navigation/native expo-navigation-bar expo-system-ui react-native-safe-area-context react-native-size-matters react-native-reanimated
Important: Make sure to follow the react-native-reanimated installation guide for platform-specific setup as it requires additional configuration for iOS and Android.
1. Initialize the library (optional but recommended):
`tsx
import { initialize } from "@hoddy-ui/core";
initialize({
// Google Maps API key for Locator component
googleMapApiKey: "your-google-maps-api-key",
// Edge-to-edge display (affects Android navigation bar styling)
edgeToEdge: false,
// Custom colors
colors: {
primary: {
main: "#6366f1",
light: "#818cf8",
dark: "#4f46e5",
},
},
// Typography settings
typography: {
fontFamily: "Inter-Regular",
fontWeights: {
400: "Inter-Regular",
500: "Inter-Medium",
600: "Inter-SemiBold",
700: "Inter-Bold",
},
},
});
`
2. Wrap your app with the theme provider:
`tsx
import React from "react";
import { UIThemeProvider } from "@hoddy-ui/core";
import App from "./App";
export default function Root() {
return (
);
}
`
3. Start using components:
`tsx
import React from "react";
import { View } from "react-native";
import {
Typography,
Button,
TextField,
Animator,
useColors,
useFadeAnimation,
} from "@hoddy-ui/core";
export default function HomeScreen() {
const colors = useColors();
return (
Welcome to Hoddy UI!
variant="outlined"
keyboardType="email-address"
gutterBottom={16}
/>
title="Get Started"
variant="contained"
color="primary"
onPress={() => console.log("Button pressed!")}
/>
);
}
`
Use the initialize function to configure the library globally:
`tsx
import { initialize } from "@hoddy-ui/core";
initialize({
// Google Maps API key for map components
googleMapApiKey?: string;
// Custom color palette overrides
colors?: {
primary?: { main: string; light?: string; dark?: string };
secondary?: { main: string; light?: string; dark?: string };
// ... and more color options
};
// Enable edge-to-edge display mode
edgeToEdge?: boolean;
// Typography settings
typography?: {
// Primary font family
fontFamily?: string;
// Font family mappings for each weight (Android support)
fontWeights?: {
100?: string;
200?: string;
300?: string;
400?: string;
500?: string;
600?: string;
700?: string;
800?: string;
900?: string;
};
};
});
`
`tsx`
initialize({
googleMapApiKey: "AIzaSyBxxxxxxxxxxxxxxxxxxxxxx",
edgeToEdge: true,
colors: {
primary: "#007AFF",
secondary: "#34C759",
},
typography: {
fontFamily: "Inter",
fontWeights: {
400: "Inter-Regular",
500: "Inter-Medium",
600: "Inter-SemiBold",
700: "Inter-Bold",
},
},
});
Note: The fontWeights property is particularly useful for Android devices where different font weights require separate font family files. This allows you to map each weight (100-900) to its corresponding font family name.
The theme system automatically detects system preferences and can be controlled programmatically:
`tsx
import { useTheme } from "@hoddy-ui/core";
function ThemeToggle() {
const { themeState, themeDispatch } = useTheme();
const toggleTheme = () => {
themeDispatch({
type: themeState.mode === "dark" ? "light" : "dark",
});
};
return (
title={Switch to ${themeState.mode === "dark" ? "Light" : "Dark"} Mode}`
onPress={toggleTheme}
/>
);
}
Access theme colors and state throughout your app:
`tsx
import { useColors, useTheme } from "@hoddy-ui/core";
function MyComponent() {
const colors = useColors();
const theme = useTheme();
return (
backgroundColor: colors.white[1],
borderColor: colors.primary.main,
}}
>
Current theme: {theme}
);
}
`
The library includes a comprehensive color system:
`tsx
const colors = {
// Primary colors
primary: { main: "#007AFF", light: "#5AC8FA", dark: "#0051D5" },
secondary: { main: "#FF3B30", light: "#FF6B6B", dark: "#D70015" },
// Neutral colors
white: ["#FFFFFF", "#F8F9FA", "#E9ECEF"],
black: ["#000000", "#212529", "#495057"],
// Semantic colors
success: { main: "#28A745", light: "#34CE57", dark: "#1E7E34" },
error: { main: "#DC3545", light: "#E74C3C", dark: "#C82333" },
warning: { main: "#FFC107", light: "#FFD54F", dark: "#FF8F00" },
// Utility colors
textPrimary: { main: "#212529" },
textSecondary: { main: "#6C757D" },
divider: { main: "#DEE2E6" },
};
`
- SafeAreaView - Safe area wrapper for different devices
- Grid & GridItem - Flexible grid layout system
- FormWrapper - Form container with validation support
- AdaptiveStatusBar - Theme-aware status bar
- Navigation utilities via hooks
- Typography - Comprehensive text component with variants (h1-h6, body1-body2, caption)
- TextField - Material Design text input with variants
- TextField2 - Alternative text field design
- OTPInput - One-Time Password input with auto-advance and paste support
- Locator - Location picker with Google Maps integration
- Button - Primary action buttons with variants
- IconButton - Icon-only buttons
- LinkButton - Text-based link buttons
- FlashMessage - Toast notifications and alerts
- AlertX - Customizable alert dialogs
- Spinner - Loading indicators
- Avatar - User profile images and placeholders
- List, ListItem, ListItemText - List components
- Popup - Modal dialogs and bottom sheets
- Animator - Layout animations and transitions
A versatile text component supporting multiple variants and styling options.
Props:
| Prop | Type | Default | Description |
| -------------- | --------------------------------------------------------------------------------- | --------- | ----------------------------- |
| variant | "h1" \| "h2" \| "h3" \| "h4" \| "h5" \| "h6" \| "body1" \| "body2" \| "caption" | "body1" | Text style variant |color
| | string | "dark" | Text color from theme palette |align
| | "left" \| "center" \| "right" | "left" | Text alignment |gutterBottom
| | number | 0 | Bottom margin in pixels |fontWeight
| | number | 400 | Font weight |textCase
| | "uppercase" \| "lowercase" \| "capitalize" | null | Text transformation |
Example:
`tsx`
Welcome to Hoddy UI
A comprehensive button component with multiple variants and states.
Props:
| Prop | Type | Default | Description |
| ---------- | ------------------------------------- | ------------- | -------------------------- |
| title | string | - | Button text |variant
| | "contained" \| "outlined" \| "text" | "contained" | Button variant |color
| | string | "primary" | Button color from theme |size
| | "small" \| "medium" \| "large" | "medium" | Button size |disabled
| | boolean | false | Disabled state |loading
| | boolean | false | Loading state with spinner |onPress
| | () => void | - | Press handler |start
| | ReactNode | - | Leading icon/element |end
| | ReactNode | - | Trailing icon/element |
Example:
`tsx`
title="Submit"
variant="contained"
color="primary"
loading={isLoading}
onPress={handleSubmit}
/>
A button component that displays only an icon.
Props:
| Prop | Type | Default | Description |
| ----------------- | --------------------- | ------------ | ------------------------- |
| icon | string | - | Icon name |iconType
| | "material" \| "ion" | "material" | Icon library to use |size
| | number | 24 | Icon size in pixels |color
| | string | "dark" | Icon color from theme |bg
| | boolean | false | Show background circle |elevation
| | number | 0 | Shadow elevation |disabled
| | boolean | false | Disabled state |onPress
| | () => void | - | Press handler |style
| | ViewStyle | {} | Icon style overrides |containerStyles
| | ViewStyle | {} | Container style overrides |
Example:
`tsx`
iconType="ion"
size={28}
color="primary"
bg={true}
elevation={2}
onPress={() => console.log("Starred!")}
/>
A text-based button component for navigation or secondary actions.
Props:
| Prop | Type | Default | Description |
| ------------ | ------------ | -------- | --------------------- |
| title | string | - | Button text |color
| | string | "blue" | Text color from theme |fontSize
| | number | 12 | Font size |fontWeight
| | string | "400" | Font weight |disabled
| | boolean | false | Disabled state |onPress
| | () => void | - | Press handler |style
| | TextStyle | {} | Text style overrides |
Example:
`tsx`
color="primary"
fontSize={14}
onPress={() => navigation.navigate("ForgotPassword")}
/>
A Material Design text input component with comprehensive features.
Props:
| Prop | Type | Default | Description |
| -------------- | -------------------------------------- | ------------ | --------------------- |
| label | string | - | Input label |variant
| | "outlined" \| "filled" \| "standard" | "outlined" | Input variant |value
| | string | - | Input value |onChangeText
| | (text: string) => void | - | Change handler |placeholder
| | string | - | Placeholder text |error
| | boolean | false | Error state |helperText
| | string | - | Helper/error text |disabled
| | boolean | false | Disabled state |multiline
| | boolean | false | Multiline input |start
| | ReactNode | - | Leading icon/element |end
| | ReactNode | - | Trailing icon/element |
Example:
`tsx`
variant="outlined"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
error={!!emailError}
helperText={emailError}
/>
A specialized input component for One-Time Password entry with auto-advance, paste support, and backspace handling.
Props:
| Prop | Type | Default | Description |
| ---------- | ------------------------------------- | ------------ | -------------------------------- |
| length | number | 6 | Number of OTP digits |onChange
| | (value: string) => void | - | Change handler for OTP value |value
| | string | "" | Current OTP value |variant
| | "outlined" \| "text" \| "contained" | "outlined" | Input variant style |spacing
| | number | 1 | Spacing between input boxes |size
| | number | 45 | Size of each input box in pixels |
Features:
- Auto-advance: Automatically moves to next input after digit entry
- Paste support: Handles pasting of complete OTP codes
- Backspace handling: Moves to previous input on backspace
- Number-only input: Automatically filters non-numeric characters
- Customizable styling: Supports different variants and sizes
Example:
`tsx
const [otp, setOtp] = useState('');
value={otp}
onChange={setOtp}
variant="outlined"
size={50}
spacing={2}
/>
// Usage with form validation
value={otp}
onChange={(value) => {
setOtp(value);
if (value.length === 4) {
verifyOTP(value);
}
}}
variant="contained"
/>
`
A location picker component with Google Maps integration.
Props:
| Prop | Type | Default | Description |
| -------------------- | ---------------------------------- | ------------- | -------------------------- |
| onLocationSelected | (location: LocationType) => void | - | Location selection handler |label
| | string | - | Input label |variant
| | "contained" \| "outlined" | "contained" | Input variant |location
| | LocationType | - | Current location |country
| | string | "ng" | Country code for search |error
| | boolean | false | Error state |
Example:
`tsx`
onLocationSelected={(location) => setSelectedLocation(location)}
country="us"
/>
A safe area wrapper component that handles device-specific safe areas.
Props:
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ------------------------- |
| children | ReactNode | - | Child components |style
| | ViewStyle | {} | Container style overrides |
Example:
`tsx`
A status bar component that automatically adapts to the current theme.
Props:
| Prop | Type | Default | Description |
| ------------- | --------- | ------- | ------------------------------------- |
| translucent | boolean | false | Whether the status bar is translucent |
Example:
`tsx`
A customizable alert component for displaying important messages.
Props:
| Prop | Type | Default | Description |
| -------------- | --------------------------------------------- | ------------- | ------------------------- |
| type | "info" \| "warning" \| "success" \| "error" | "info" | Alert type |variant
| | "contained" \| "outlined" | "contained" | Alert variant |title
| | string | - | Alert title |body
| | string | - | Alert message |gutterBottom
| | number | 0 | Bottom margin in pixels |style
| | ViewStyle | {} | Container style overrides |
Example:
`tsx`
title="Success!"
body="Your profile has been updated successfully."
gutterBottom={20}
/>
A component for displaying user avatars with support for images, labels, or default icons.
Props:
| Prop | Type | Default | Description |
| --------- | --------------------------- | ------------- | --------------------------- |
| source | ImageSourcePropType | - | Image source |label
| | string | - | Text label (first letter) |size
| | number | 48 | Avatar size in pixels |color
| | string | "dark" | Background color from theme |variant
| | "contained" \| "outlined" | "contained" | Avatar variant |style
| | ViewStyle | {} | Container style overrides |
Example:
`tsx
size={64}
/>
color="primary"
size={48}
/>
`
Flexible grid layout components for organizing content.
Grid Props:
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ------------------------- |
| children | ReactNode | - | GridItem components |spacing
| | number | 1 | Spacing between items |style
| | ViewStyle | {} | Container style overrides |
GridItem Props:
| Prop | Type | Default | Description |
| ------------ | ---------------------------------------- | ------- | ------------------------- |
| children | ReactNode | - | Item content |col
| | number | 2 | Number of columns to span |alignItems
| | "center" \| "flex-start" \| "flex-end" | - | Item alignment |spacing
| | number | 1 | Item spacing |style
| | ViewStyle | {} | Item style overrides |
Example:
`tsx`
A wrapper component that provides keyboard handling and scrolling for forms.
Props:
| Prop | Type | Default | Description |
| ------------------------ | ------------------------------- | ------------------ | --------------------------- |
| children | ReactNode | - | Form content |mode
| | "scroll" \| "static" | "scroll" | Wrapper mode |behavior
| | KeyboardAvoidingView behavior | Platform-dependent | Keyboard avoidance behavior |keyboardVerticalOffset
| | number | 10 | Keyboard offset |contentContainerStyle
| | ViewStyle | {} | ScrollView content style |style
| | ViewStyle | {} | Container style |onScroll
| | (event) => void | - | Scroll event handler |
Example:
`tsx`
Components for displaying structured lists of data.
List Props:
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ------------------------- |
| children | ReactNode | - | ListItem components |style
| | ViewStyle | {} | Container style overrides |
ListItem Props:
| Prop | Type | Default | Description |
| ---------- | ------------ | ------- | ------------------------- |
| children | ReactNode | - | Item content |link
| | boolean | false | Show arrow indicator |divider
| | boolean | false | Show bottom border |onPress
| | () => void | - | Press handler |index
| | number | 1 | Item index for animations |style
| | ViewStyle | {} | Item style overrides |
ListItemText Props:
| Prop | Type | Default | Description |
| ---------------- | ----------------- | ------- | ------------------------- |
| primary | string | - | Primary text |secondary
| | string | - | Secondary text |divider
| | boolean | false | Show bottom border |primaryProps
| | TypographyProps | {} | Primary text props |secondaryProps
| | TypographyProps | {} | Secondary text props |style
| | ViewStyle | {} | Container style overrides |
Example:
`tsx`
A modal component for overlays, dialogs, and bottom sheets.
Props:
| Prop | Type | Default | Description |
| ------------------------ | ------------------- | ------- | ------------------------- |
| open | boolean | - | Whether popup is visible |onClose
| | () => void | - | Close handler |title
| | string | - | Popup title |children
| | ReactNode | - | Popup content |sheet
| | boolean \| number | false | Bottom sheet mode/height |bare
| | boolean | false | Hide header and padding |keyboardVerticalOffset
| | number | - | Keyboard avoidance offset |onModalShow
| | () => void | - | Callback when modal shows |onModalHide
| | () => void | - | Callback when modal hides |style
| | ViewStyle | {} | Container style overrides |
Example:
`tsx`
onClose={() => setIsOpen(false)}
title="Confirm Action"
sheet={400}
onModalShow={() => console.log("Modal is now visible")}
onModalHide={() => console.log("Modal is now hidden")}
>
A loading indicator component with customizable appearance.
Props:
| Prop | Type | Default | Description |
| ------------ | -------------------- | ----------- | ------------------------- |
| size | "small" \| "large" | "large" | Spinner size |color
| | string | "primary" | Spinner color from theme |label
| | string | - | Loading text |fullscreen
| | boolean | false | Cover entire screen |style
| | ViewStyle | {} | Container style overrides |
Example:
`tsx`
A unified component that provides a single interface for all animation types with generic props. Built with react-native-reanimated for optimal performance and smooth animations.
Features:
- Single Component: One component handles all animation types
- Generic Props: Consistent prop naming across all animations (e.g., closeAfter instead of animation-specific names)
- Modular Hooks: Animation logic is separated into individual custom hooks
- Type Safety: Full TypeScript support with proper typing
- Performance: Built with react-native-reanimated for native performance
- Smooth Animations: Runs on the UI thread for 60fps animations
Requirements:
Requires react-native-reanimated as peer dependencies. Make sure to follow the react-native-reanimated installation guide for platform-specific setup.
Generic Props:
All animation types support these generic props:
| Prop | Type | Default | Description |
| ------------ | --------------------------------------------------------------------------- | ------- | ------------------------------------------------------------- |
| type | "fade" \| "grow" \| "slide" \| "blink" \| "float" \| "roll" \| "thrownup" | - | Animation type |duration
| | number | 500 | Animation duration in milliseconds |delay
| | number | 0 | Delay before animation starts |closeAfter
| | number \| null | null | Time after which the exit animation starts (null for no exit) |style
| | ViewStyle | {} | Additional styles for the animated view |
Animation-Specific Props:
Slide Animation:
- direction: "up" \| "down" \| "left" \| "right"initialValue
- : Custom initial position value
Grow Animation:
- initialScale: Starting scale (default: 0)
Blink Animation:
- blinkDuration: Duration of one blink cycleminOpacity
- : Minimum opacity valuemaxOpacity
- : Maximum opacity value
Float Animation:
- closeDuration: Duration of exit animationfloatDistance
- : Distance to float up/downfloatDuration
- : Duration of one float cycle
Roll Animation:
- initialTranslateY: Initial vertical positioninitialRotate
- : Initial rotation value
Examples:
`tsx
// Fade animation
// Slide animation
// Grow animation
// Blink animation (continuous)
// Float animation
// Roll animation
// Thrown up animation
`
Available Animation Types:
1. fade: Simple fade in/out using opacity (native performance)
2. grow: Scale-based growth animation with easing
3. slide: Directional slide animations from screen edges
4. blink: Continuous opacity blinking with repeat
5. float: Floating up/down motion with fade effects
6. roll: Combined rotation and translation effects
7. thrownup: Spring-based upward animation with physics
All animations run on the UI thread for optimal performance and smooth 60fps animations.
Using Animation Hooks Directly:
You can also use the animation hooks directly for custom implementations with react-native-reanimated:
`tsx
import { useFadeAnimation, useSlideAnimation } from "@hoddy-ui/core";
import Animated from "react-native-reanimated";
const MyComponent = () => {
const { animatedStyle } = useFadeAnimation({
duration: 800,
closeAfter: 2000,
});
return (
);
};
`
Performance Benefits:
With react-native-reanimated, all animations:
- Run on the UI thread for better performance
- Maintain 60fps even during JavaScript thread blocking
- Use native drivers automatically
- Support worklets for complex animation logic
- Provide smooth gestures and interactions
Migration from Legacy API:
Replace old individual animation components:
`tsx
// Old way
// New way
`
Display toast notifications and alerts.
Props:
| Prop | Type | Default | Description |
| ---------- | --------------------------------------------- | -------- | ---------------------- |
| message | string | - | Message text |type
| | "success" \| "error" \| "warning" \| "info" | "info" | Message type |duration
| | number | 3000 | Display duration in ms |position
| | "top" \| "bottom" | "top" | Message position |
Usage:
`tsx
import { showMessage } from "@hoddy-ui/core";
// Show a success message
showMessage({
message: "Profile updated successfully!",
type: "success",
});
`
Access the current theme's color palette:
`tsx
import { useColors } from "@hoddy-ui/core";
function MyComponent() {
const colors = useColors();
return (
{/ Component content /}
);
}
`
Access and control the current theme:
`tsx
import { useTheme } from "@hoddy-ui/core";
function ThemeAwareComponent() {
const { themeState, themeDispatch } = useTheme();
const toggleTheme = () => {
themeDispatch({
type: themeState.mode === "dark" ? "light" : "dark",
});
};
return ;
}
`
Get theme-aware navigation screen options:
`tsx
import { useNavScreenOptions } from "@hoddy-ui/core";
function MyScreen() {
const screenOptions = useNavScreenOptions("stack");
// Use with React Navigation
return (
);
}
`
Access animation logic directly for custom implementations using react-native-reanimated:
#### useFadeAnimation
`tsx
import { useFadeAnimation } from "@hoddy-ui/core";
import Animated from "react-native-reanimated";
function FadeComponent() {
const { animatedStyle } = useFadeAnimation({
duration: 800,
closeAfter: 2000,
});
return (
);
}
`
#### useSlideAnimation
`tsx
import { useSlideAnimation } from "@hoddy-ui/core";
import Animated from "react-native-reanimated";
function SlideComponent() {
const { animatedStyle } = useSlideAnimation({
direction: "up",
duration: 600,
initialValue: 100,
});
return (
);
}
`
#### useGrowAnimation
`tsx
import { useGrowAnimation } from "@hoddy-ui/core";
import Animated from "react-native-reanimated";
function GrowComponent() {
const { animatedStyle } = useGrowAnimation({
duration: 500,
initialScale: 0.5,
});
return (
);
}
`
#### Other Animation Hooks
- useBlinkAnimation - For continuous blinking effectsuseFloatAnimation
- - For floating up/down motionuseRollAnimation
- - For rotation and translation effectsuseThrownUpAnimation
- - For spring-based upward animations
All animation hooks accept similar configuration objects with properties like duration, delay, and animation-specific options. They return animatedStyle objects that work with Animated.View from react-native-reanimated for optimal performance.
Create your own theme provider with custom configurations:
`tsx
import { UIThemeProvider, initialize } from "@hoddy-ui/core";
// Initialize with custom configuration
initialize({
fontFamily: "CustomFont-Regular",
colors: {
primary: { main: "#FF6B6B" },
secondary: { main: "#4ECDC4" },
},
});
function App() {
return (
);
}
`
`tsx
import React, { useState } from "react";
import { View } from "react-native";
import { TextField, Button, FormWrapper } from "@hoddy-ui/core";
function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [errors, setErrors] = useState({});
const validate = () => {
const newErrors = {};
if (!email) newErrors.email = "Email is required";
if (!password) newErrors.password = "Password is required";
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = () => {
if (validate()) {
// Handle form submission
}
};
return (
value={email}
onChangeText={setEmail}
error={!!errors.email}
helperText={errors.email}
keyboardType="email-address"
/>
value={password}
onChangeText={setPassword}
error={!!errors.password}
helperText={errors.password}
secureTextEntry
/>
title="Login"
onPress={handleSubmit}
variant="contained"
color="primary"
/>
);
}
``
- React Native: ≥ 0.71.8
- Expo: SDK 48+
- iOS: 11.0+
- Android: API 21+ (Android 5.0)
- TypeScript: ≥ 5.0.4
We welcome contributions! Please see our Contributing Guide for details.
MIT © Hoddy Inc
- GitHub Repository
- npm Package
- Issues & Support
---
Need help? Open an issue or check out our examples.