@shiplypalestine/ui
v1.2.10
Published
A comprehensive React Native UI component library with a modern design system.
Downloads
449
Readme
Shiply UI Library
A comprehensive React Native UI component library with a modern design system.
Features
- 🎨 Theme support (Light/Dark mode)
- 🌐 RTL (Right-to-Left) support
- 📱 Cross-platform (iOS, Android, Web)
- 🎯 TypeScript support
- 🎨 NativeWind for styling
- 🔧 Customizable components
Installation
npm install @shiplypalestine/ui
# or
yarn add @shiplypalestine/uiRequired Setup
- Install peer dependencies:
npm install nativewind@^2.0.11 tailwindcss@^3.3.2 postcss@^8.4.23 autoprefixer@^10.4.14 @react-native-async-storage/async-storage
# or
yarn add nativewind@^2.0.11 tailwindcss@^3.3.2 postcss@^8.4.23 autoprefixer@^10.4.14 @react-native-async-storage/async-storage- For React Native CLI projects, run:
npx pod-install ios- For Expo projects, run:
npx expo prebuild- Configure your
tailwind.config.js:
module.exports = {
content: [
"./App.{js,jsx,ts,tsx}",
"./src/**/*.{js,jsx,ts,tsx}",
"./node_modules/@shiplypalestine/ui/**/*.{js,jsx,ts,tsx}"
],
theme: {
extend: {
colors: {
primary: {
DEFAULT: '#007AFF',
dark: '#0056B3',
},
secondary: {
DEFAULT: '#5856D6',
dark: '#3634A3',
},
accent: {
DEFAULT: '#FF2D55',
dark: '#D6002F',
},
background: {
light: '#FFFFFF',
dark: '#121212',
},
text: {
light: '#000000',
dark: '#FFFFFF',
},
},
},
},
plugins: [],
}- Add the following to your
babel.config.js:
module.exports = {
plugins: ["nativewind/babel"],
};Usage
Theme Provider
Wrap your app with the ThemeProvider to enable theming and RTL support:
import { ThemeProvider } from '@shiplypalestine/ui';
import { SafeAreaProvider } from 'react-native-safe-area-context';
export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider>
{/* Your app content */}
</ThemeProvider>
</SafeAreaProvider>
);
}Note on Safe Area:
Components like Toast use SafeAreaView internally to avoid rendering under device notches or home indicators. For SafeAreaView to work correctly, your entire application must be wrapped with a single <SafeAreaProvider> at its root. Make sure you have react-native-safe-area-context installed and configured as shown in the example above.
Hooks
useDisclose
A utility hook for managing open/close state of components like modals, action sheets, and dropdowns.
import { useDisclose } from '@shiplypalestine/ui';
export function MyComponent() {
const { isOpen, onOpen, onClose, onToggle } = useDisclose();
return (
<View>
<Button title="Open Modal" onPress={onOpen} />
<Modal visible={isOpen} onClose={onClose}>
<Text>Modal content</Text>
<Button title="Close" onPress={onClose} />
</Modal>
</View>
);
}Hook API:
isOpen: boolean- Current open/close stateonOpen: () => void- Function to openonClose: () => void- Function to closeonToggle: () => void- Function to toggle state
Initial State:
// Start with closed state (default)
const { isOpen, onOpen, onClose } = useDisclose();
// Start with open state
const { isOpen, onOpen, onClose } = useDisclose(true);Components
Import and use components as needed:
import { Button, Input, Card, Modal, Select } from '@shiplypalestine/ui';
export function MyComponent() {
const [isModalVisible, setIsModalVisible] = useState(false);
const [inputValue, setInputValue] = useState('');
const [selectedOption, setSelectedOption] = useState('');
return (
<View style={{ padding: 16 }}>
{/* Button Component */}
<Button
title="Click Me"
onPress={() => setIsModalVisible(true)}
variant="primary"
size="medium"
/>
{/* Input Component */}
<Input
value={inputValue}
onChangeText={setInputValue}
placeholder="Enter text..."
label="Example Input"
/>
{/* Card Component */}
<Card title="Example Card">
<Text>This is a card component</Text>
</Card>
{/* Select Component */}
<Select
options={[
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2' },
{ label: 'Option 3', value: '3' },
]}
onSelect={(value) => setSelectedOption(value)}
placeholder="Select an option"
/>
{/* Modal Component */}
<Modal
visible={isModalVisible}
onClose={() => setIsModalVisible(false)}
title="Example Modal"
>
<Text>This is a modal component</Text>
<Button
title="Close"
onPress={() => setIsModalVisible(false)}
variant="secondary"
/>
</Modal>
</View>
);
}Style Customization
All components support both NativeWind classes and StyleSheet props for customization. This allows you to override default styles using either approach:
Using StyleSheet Props
import { Button } from '@shiplypalestine/ui';
import { StyleSheet } from 'react-native';
export function MyComponent() {
return (
<Button
title="Custom Button"
onPress={() => {}}
style={styles.button}
textStyle={styles.buttonText}
/>
);
}
const styles = StyleSheet.create({
button: {
backgroundColor: 'purple',
borderRadius: 20,
},
buttonText: {
fontSize: 18,
fontWeight: 'bold',
},
});Using NativeWind Classes
import { Button } from '@shiplypalestine/ui';
export function MyComponent() {
return (
<Button
title="Custom Button"
onPress={() => {}}
className="bg-purple-500 rounded-full"
textClassName="text-lg font-bold"
/>
);
}Available Components
Button
A customizable button component with multiple variants, states, and icon support.
Props:
title: string (required)onPress: () => void (required)variant: 'primary' | 'secondary' | 'outline' (optional)size: 'small' | 'medium' | 'large' (optional)disabled: boolean (optional)loading: boolean (optional)startIcon: React.ReactNode (optional) - Icon to display before textendIcon: React.ReactNode (optional) - Icon to display after texticonSpacing: number (optional) - Space between icon and text (default: 8)style: ViewStyle (optional)textStyle: TextStyle (optional)className: string (optional)textClassName: string (optional)
Example usage:
import { Button } from '@shiplypalestine/ui';
// Basic usage
<Button title="Click Me" onPress={() => {}} />
// With icons
<Button
title="Next"
onPress={() => {}}
endIcon={<MyIcon name="arrow-right" />}
/>
<Button
title="Back"
onPress={() => {}}
startIcon={<MyIcon name="arrow-left" />}
/>
// With custom icon spacing
<Button
title="Download"
onPress={() => {}}
startIcon={<MyIcon name="download" />}
iconSpacing={12}
/>Input
A customizable input component with label and error support.
Props:
label: string (optional)error: string (optional)disabled: boolean (optional)multiline: boolean (optional)containerStyle: ViewStyle (optional)inputStyle: TextStyle (optional)labelStyle: TextStyle (optional)errorStyle: TextStyle (optional)containerClassName: string (optional)inputClassName: string (optional)labelClassName: string (optional)errorClassName: string (optional)startComponent: ReactNode (optional) - Component to display at the start of the inputstartComponentContainerStyle: ViewStyle (optional) - Style for the startComponent containerstartComponentContainerClassName: string (optional) - Classes for the startComponent containerforceRTL: boolean (optional) - Force right-to-left text directionforceLTR: boolean (optional) - Force left-to-right text direction- All other TextInput props are supported
Example usage:
import { Input } from '@shiplypalestine/ui';
// Basic usage
<Input placeholder="Enter text" />
// With label and error
<Input
label="Email"
error="Invalid email"
placeholder="Enter your email"
/>
// With start component (icon or other element)
<Input
label="Search"
placeholder="Search..."
startComponent={<SearchIcon size={20} color="#007AFF" />}
/>
// With start component in RTL mode
<Input
label="بحث"
placeholder="أدخل كلمة البحث..."
forceRTL={true}
startComponent={<SearchIcon size={20} color="#007AFF" />}
/>
// With custom styles
<Input
placeholder="Custom input"
containerStyle={{ marginBottom: 20 }}
inputStyle={{ fontSize: 16 }}
labelStyle={{ color: 'blue' }}
containerClassName="bg-gray-100"
inputClassName="border-2"
/>
// With styled start component
<Input
label="Username"
placeholder="Enter username"
startComponent={
<Text style={{ color: '#5856D6', fontWeight: 'bold' }}>@</Text>
}
startComponentContainerClassName="bg-gray-100 h-full rounded-l-lg border-r"
/>
// Disabled input
<Input
placeholder="Disabled input"
disabled
/>
// Multiline input
<Input
placeholder="Enter multiple lines"
multiline
/>TextArea
A multi-line text input component with advanced features like auto-growing, character counting, and customizable styling. Extends TextInput functionality with TextArea-specific props.
import { TextArea } from '@shiplypalestine/ui';
// Basic usage
<TextArea
value={value}
onChangeText={setValue}
placeholder="Enter your message here..."
label="Message"
/>
// With auto-growing
<TextArea
value={value}
onChangeText={setValue}
placeholder="This will grow as you type..."
label="Auto-growing Message"
autoGrow={true}
minRows={3}
maxRows={8}
/>
// With character count
<TextArea
value={value}
onChangeText={setValue}
placeholder="Type something (max 200 characters)..."
label="Limited Message"
maxLength={200}
showCharacterCount={true}
rows={4}
/>
// With error state
<TextArea
value={value}
onChangeText={setValue}
placeholder="Enter your message..."
label="Message"
error="This field is required"
/>
// With start component
<TextArea
value={value}
onChangeText={setValue}
placeholder="Enter your message..."
label="Message with Icon"
startComponent={<Text style={{ color: 'blue' }}>📝</Text>}
/>
// RTL support
<TextArea
value={value}
onChangeText={setValue}
placeholder="اكتب رسالتك هنا..."
label="رسالة"
forceRTL={true}
rows={3}
/>
// Custom styling
<TextArea
value={value}
onChangeText={setValue}
placeholder="Custom styled textarea..."
label="Custom Style"
containerStyle={{ backgroundColor: '#F9FAFB', padding: 16, borderRadius: 12 }}
inputStyle={{ fontSize: 18, fontStyle: 'italic' }}
labelStyle={{ color: 'blue', fontWeight: 'bold' }}
rows={5}
/>Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| value | string | '' | The current value of the textarea |
| onChangeText | (text: string) => void | - | Callback when text changes |
| placeholder | string | - | Placeholder text |
| label | string | - | Label text displayed above the textarea |
| error | string | - | Error message to display |
| disabled | boolean | false | Whether the textarea is disabled |
| rows | number | 4 | Number of visible rows |
| maxRows | number | 10 | Maximum number of rows when auto-growing |
| minRows | number | 2 | Minimum number of rows when auto-growing |
| autoGrow | boolean | false | Whether the textarea should grow with content |
| showCharacterCount | boolean | false | Whether to show character count |
| maxLength | number | - | Maximum number of characters allowed |
| resize | 'none' \| 'vertical' \| 'horizontal' \| 'both' | 'vertical' | Resize behavior (web only) |
| forceRTL | boolean | false | Force right-to-left text direction |
| forceLTR | boolean | false | Force left-to-right text direction |
Style Props
| Prop | Type | Description |
|------|------|-------------|
| style | ViewStyle | Custom style for the container |
| className | string | NativeWind classes for the container |
| containerStyle | ViewStyle | Custom style for the main container |
| containerClassName | string | NativeWind classes for the main container |
| inputStyle | TextStyle | Custom style for the text input |
| inputClassName | string | NativeWind classes for the text input |
| labelStyle | TextStyle | Custom style for the label |
| labelClassName | string | NativeWind classes for the label |
| errorStyle | TextStyle | Custom style for the error message |
| errorClassName | string | NativeWind classes for the error message |
| characterCountStyle | TextStyle | Custom style for the character count |
| characterCountClassName | string | NativeWind classes for the character count |
Start/End Component Props
| Prop | Type | Description |
|------|------|-------------|
| startComponent | React.ReactNode | Component to display at the start of the textarea |
| startComponentContainerStyle | ViewStyle | Style for the start component container |
| startComponentContainerClassName | string | Classes for the start component container |
| endComponent | React.ReactNode | Component to display at the end of the textarea |
| endComponentContainerStyle | ViewStyle | Style for the end component container |
| endComponentContainerClassName | string | Classes for the end component container |
Features
- Multi-line Support: Built-in multiline text input
- Auto-growing: Automatically resize based on content
- Character Counting: Display character count with max length
- RTL Support: Right-to-left text direction support
- Start/End Components: Add icons or other components
- Custom Styling: Full style customization support
- Error Handling: Built-in error state and messaging
- Accessibility: Proper accessibility support
- Cross-platform: Works on web, iOS, and Android
Card
A customizable card component with title and content.
Props:
title: string (optional)children: React.ReactNode (required)variant: 'elevated' | 'outlined' | 'filled' (optional)padding: 'none' | 'small' | 'medium' | 'large' (optional)onPress: () => void (optional)style: ViewStyle (optional)titleStyle: TextStyle (optional)className: string (optional)titleClassName: string (optional)
Modal
A customizable modal component with title and close button.
Props:
visible: boolean (required)onClose: () => void (required)title: string (optional)children: React.ReactNode (required)showCloseButton: boolean (optional)animationType: 'fade' | 'slide' (optional)containerStyle: ViewStyle (optional)contentStyle: ViewStyle (optional)titleStyle: TextStyle (optional)closeButtonStyle: ViewStyle (optional)closeButtonTextStyle: TextStyle (optional)containerClassName: string (optional)contentClassName: string (optional)titleClassName: string (optional)closeButtonClassName: string (optional)closeButtonTextClassName: string (optional)
Select
A customizable select component with dropdown options.
Props:
options: Array<{ label: string, value: string }> (required)onSelect: (value: string) => void (required)placeholder: string (optional)disabled: boolean (optional)value: string (optional) - Initial selected valuecontainerStyle: ViewStyle (optional)triggerStyle: ViewStyle (optional)triggerTextStyle: TextStyle (optional)dropdownStyle: ViewStyle (optional)optionStyle: ViewStyle (optional)optionTextStyle: TextStyle (optional)containerClassName: string (optional)triggerClassName: string (optional)triggerTextClassName: string (optional)dropdownClassName: string (optional)optionClassName: string (optional)optionTextClassName: string (optional)
Radio & RadioGroup
Customizable radio components with group support.
Props for Radio:
value: string (required)label: string (required)selected: boolean (required)onSelect: (value: string) => void (required)disabled: boolean (optional)containerStyle: ViewStyle (optional)radioStyle: ViewStyle (optional)labelStyle: TextStyle (optional)containerClassName: string (optional)radioClassName: string (optional)labelClassName: string (optional)
Props for RadioGroup:
value: string (required)onChange: (value: string) => void (required)children: React.ReactNode (required)containerStyle: ViewStyle (optional)containerClassName: string (optional)
Checkbox & CheckboxGroup
Customizable checkbox components with group support.
Props for Checkbox:
value: string (required)label: string (required)checked: boolean (required)onCheck: (values: string[]) => void (required)disabled: boolean (optional)containerStyle: ViewStyle (optional)checkboxStyle: ViewStyle (optional)labelStyle: TextStyle (optional)checkmarkStyle: TextStyle (optional)containerClassName: string (optional)checkboxClassName: string (optional)labelClassName: string (optional)checkmarkClassName: string (optional)
Props for CheckboxGroup:
value: string[] (required)onChange: (values: string[]) => void (required)children: React.ReactNode (required)containerStyle: ViewStyle (optional)containerClassName: string (optional)
Progress
A customizable progress component.
Props:
value: number (required)max: number (optional)variant: 'linear' | 'circular' (optional)color: 'primary' | 'secondary' | 'accent' (optional)size: 'small' | 'medium' | 'large' (optional)showValue: boolean (optional)containerStyle: ViewStyle (optional)trackStyle: ViewStyle (optional)progressStyle: ViewStyle (optional)valueStyle: TextStyle (optional)containerClassName: string (optional)trackClassName: string (optional)progressClassName: string (optional)valueClassName: string (optional)
Spinner
A customizable spinner component.
Props:
size: 'small' | 'medium' | 'large' (optional)color: 'primary' | 'secondary' | 'accent' | 'white' (optional)fullScreen: boolean (optional)containerStyle: ViewStyle (optional)spinnerStyle: ViewStyle (optional)containerClassName: string (optional)spinnerClassName: string (optional)
Switch
A customizable switch component.
Props:
value: boolean (required)onValueChange: (value: boolean) => void (required)disabled: boolean (optional)style: ViewStyle (optional)className: string (optional)trackColorFalse: string (optional)trackColorTrue: string (optional)thumbColor: string (optional)
Toast
A flexible toast notification system that supports multiple usage patterns: context-based, hook-based, and direct function calls.
Usage Patterns
- Using the
useToasthook (Recommended):
import { useToast } from '@shiplypalestine/ui';
function MyComponent() {
const { showToast } = useToast();
const handleSuccess = () => {
showToast({
message: 'Operation completed successfully!',
type: 'success',
duration: 3000,
position: 'top'
});
};
return (
<Button
title="Show Success Toast"
onPress={handleSuccess}
/>
);
}- Using the
ToastProvider:
import { ToastProvider } from '@shiplypalestine/ui';
function App() {
return (
<ToastProvider>
{/* Your app content */}
</ToastProvider>
);
}- Using the direct
showToastfunction (Deprecated):
import { showToast } from '@shiplypalestine/ui';
function MyComponent() {
const handleError = () => {
showToast({
message: 'An error occurred',
type: 'error'
});
};
return (
<Button
title="Show Error Toast"
onPress={handleError}
/>
);
}Toast Options
The showToast function accepts the following options:
message: string (required) - The message to displaytype: 'success' | 'error' | 'info' | 'warning' (optional) - The type of toastduration: number (optional) - Duration in milliseconds (default: 3000)position: 'top' | 'bottom' (optional) - Position of the toast (default: 'top')
Toast Types
success: Green background, used for successful operationserror: Red background, used for error messageswarning: Yellow background, used for warningsinfo: Blue background, used for general information
Example Usage
import { useToast } from '@shiplypalestine/ui';
function MyComponent() {
const { showToast } = useToast();
// Success toast
const handleSuccess = () => {
showToast({
message: 'Data saved successfully!',
type: 'success'
});
};
// Error toast
const handleError = () => {
showToast({
message: 'Failed to save data',
type: 'error'
});
};
// Warning toast
const handleWarning = () => {
showToast({
message: 'Please fill all required fields',
type: 'warning'
});
};
// Info toast
const handleInfo = () => {
showToast({
message: 'New features available',
type: 'info'
});
};
// Custom duration and position
const handleCustom = () => {
showToast({
message: 'Custom toast',
type: 'success',
duration: 5000,
position: 'bottom'
});
};
return (
<View>
<Button title="Success" onPress={handleSuccess} />
<Button title="Error" onPress={handleError} />
<Button title="Warning" onPress={handleWarning} />
<Button title="Info" onPress={handleInfo} />
<Button title="Custom" onPress={handleCustom} />
</View>
);
}Typography
A flexible text component that automatically handles text wrapping and supports various text styles.
import { Typography } from '@shiplypalestine/ui';
// Basic usage with automatic text wrapping
<Typography>
This text will automatically wrap when it reaches the end of its container.
</Typography>
// Different variants
<Typography variant="body">Body text</Typography>
<Typography variant="subtitle">Subtitle text</Typography>
<Typography variant="caption">Caption text</Typography>
<Typography variant="overline">OVERLINE TEXT</Typography>
// Different weights
<Typography weight="normal">Normal weight</Typography>
<Typography weight="medium">Medium weight</Typography>
<Typography weight="semibold">Semibold weight</Typography>
<Typography weight="bold">Bold weight</Typography>
// Text truncation with ellipsis
<Typography numberOfLines={2}>
This text will be truncated after 2 lines with an ellipsis.
</Typography>
// Different colors
<Typography color="primary">Primary color</Typography>
<Typography color="secondary">Secondary color</Typography>
<Typography color="accent">Accent color</Typography>
<Typography color="muted">Muted color</Typography>Props
variant: 'body' | 'caption' | 'overline' | 'subtitle' (default: 'body')weight: 'normal' | 'medium' | 'semibold' | 'bold' (default: 'normal')color: 'primary' | 'secondary' | 'accent' | 'default' | 'muted' | 'white' (default: 'default')align: 'left' | 'center' | 'right' (default: 'left')numberOfLines: number - Limits text to specified number of lines with ellipsisellipsizeMode: 'head' | 'middle' | 'tail' | 'clip' (default: 'tail')style: Custom styles for the textclassName: NativeWind classes
Heading
A customizable heading component for titles and section headers.
Props:
children: React.ReactNode (required)level: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' (optional)color: 'primary' | 'secondary' | 'accent' | 'default' | 'muted' | 'white' (optional)align: 'left' | 'center' | 'right' (optional)style: TextStyle (optional)className: string (optional)
Accordion & AccordionItem
Customizable accordion components for expandable content sections.
Props for Accordion:
children: React.ReactNode (required)containerStyle: ViewStyle (optional)containerClassName: string (optional)
Props for AccordionItem:
title: string (required)children: React.ReactNode (required)initiallyExpanded: boolean (optional)containerStyle: ViewStyle (optional)headerStyle: ViewStyle (optional)contentStyle: ViewStyle (optional)titleStyle: TextStyle (optional)containerClassName: string (optional)headerClassName: string (optional)contentClassName: string (optional)titleClassName: string (optional)
IconButton
A customizable icon button component that accepts any React element as an icon.
Props:
icon: React.ReactNode (required) - Any React element to display as the icononPress: () => void (required)size: 'small' | 'medium' | 'large' (optional)color: 'primary' | 'secondary' | 'accent' | 'default' | 'muted' | 'white' (optional)variant: 'solid' | 'outline' | 'ghost' (optional)disabled: boolean (optional)loading: boolean (optional)style: ViewStyle (optional)className: string (optional)
Example usage:
import { IconButton } from '@shiplypalestine/ui';
import { Feather } from '@expo/vector-icons';
// With Expo Icons
<IconButton
icon={<Feather name="heart" size={20} color="white" />}
onPress={() => {}}
color="primary"
/>
// With custom component
<IconButton
icon={<Text style={{ color: 'white', fontSize: 16 }}>⭐</Text>}
onPress={() => {}}
color="secondary"
/>
// With SVG icon
<IconButton
icon={<MySvgIcon width={20} height={20} color="white" />}
onPress={() => {}}
color="accent"
/>
// Different variants
<IconButton
icon={<Feather name="bell" size={20} color="white" />}
onPress={() => {}}
variant="solid"
color="primary"
/>
<IconButton
icon={<Feather name="bell" size={20} color="#007AFF" />}
onPress={() => {}}
variant="outline"
color="primary"
/>FlatList & SectionList
Customizable list components with built-in empty state support and performance optimizations for large data sets.
Props for both FlatList and SectionList:
containerStyle: ViewStyle (optional)contentContainerStyle: ViewStyle (optional)containerClassName: string (optional)contentContainerClassName: string (optional)showEmptyState: boolean (optional) - Default: trueemptyStateComponent: React.ReactElement (optional)
Performance Props:
initialNumToRender: number (optional) - Default: 10- Number of items to render in the initial batch
maxToRenderPerBatch: number (optional) - Default: 10- Maximum number of items to render in each batch
windowSize: number (optional) - Default: 5- Number of items to render outside the visible area
removeClippedSubviews: boolean (optional) - Default: true- Remove items that are off-screen to save memory
updateCellsBatchingPeriod: number (optional) - Default: 50- Time in ms between batch updates
onEndReachedThreshold: number (optional) - Default: 0.5- How far from the end to trigger onEndReached
getItemLayout: (data: any, index: number) => { length: number, offset: number, index: number } (optional)- Optimize rendering by providing item dimensions
Example usage with performance optimizations:
import { FlatList, SectionList } from '@shiplypalestine/ui';
// Optimized FlatList for large data sets
<FlatList
data={largeDataSet}
renderItem={({ item }) => (
<View className="p-2 bg-surface-light dark:bg-surface-dark rounded">
<Text className="text-text-light dark:text-text-dark">{item.title}</Text>
</View>
)}
keyExtractor={item => item.id}
// Performance optimizations
initialNumToRender={20}
maxToRenderPerBatch={20}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={100}
onEndReachedThreshold={0.7}
getItemLayout={(data, index) => ({
length: 60, // Height of your item
offset: 60 * index,
index,
})}
/>
// Optimized SectionList for large data sets
<SectionList
sections={largeSectionData}
renderItem={({ item }) => (
<View className="p-2 bg-surface-light dark:bg-surface-dark rounded">
<Text className="text-text-light dark:text-text-dark">{item.title}</Text>
</View>
)}
renderSectionHeader={({ section }) => (
<View className="bg-primary-light dark:bg-primary-dark p-2 rounded-t">
<Text className="text-white font-bold">{section.title}</Text>
</View>
)}
keyExtractor={item => item.id}
// Performance optimizations
initialNumToRender={20}
maxToRenderPerBatch={20}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={100}
onEndReachedThreshold={0.7}
getItemLayout={(data, index) => ({
length: 60, // Height of your item
offset: 60 * index,
index,
})}
/>Performance Tips:
- Always provide
getItemLayoutfor fixed-height items - Use
removeClippedSubviewsfor large lists - Adjust
windowSizebased on your list's complexity - Use
initialNumToRenderandmaxToRenderPerBatchto control rendering batches - Consider using
onEndReachedfor infinite scrolling - Memoize your
renderItemfunction if it's complex - Use
keyExtractorwith stable, unique keys
Box
A versatile container component that extends React Native's View with additional styling capabilities and layout features.
Props:
style: ViewStyle (optional) - Custom stylesclassName: string (optional) - NativeWind class namesbg: string (optional) - Background colorrounded: number | 'sm' | 'md' | 'lg' | 'full' (optional) - Border radiusborderWidth: number (optional) - Border widthborderColor: string (optional) - Border colorshadow: object (optional) - Shadow propertiescolor: string (optional)offset: { width: number, height: number } (optional)opacity: number (optional)radius: number (optional)elevation: number (optional)
flex: number (optional) - Flex valueflexDirection: 'row' | 'column' | 'row-reverse' | 'column-reverse' (optional)flexWrap: 'wrap' | 'nowrap' | 'wrap-reverse' (optional)justifyContent: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly' (optional)alignItems: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline' (optional)alignSelf: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline' (optional)position: 'absolute' | 'relative' (optional)top: number (optional)right: number (optional)bottom: number (optional)left: number (optional)p: number (optional) - Paddingpx: number (optional) - Horizontal paddingpy: number (optional) - Vertical paddingpt: number (optional) - Top paddingpr: number (optional) - Right paddingpb: number (optional) - Bottom paddingpl: number (optional) - Left paddingm: number (optional) - Marginmx: number (optional) - Horizontal marginmy: number (optional) - Vertical marginmt: number (optional) - Top marginmr: number (optional) - Right marginmb: number (optional) - Bottom marginml: number (optional) - Left marginw: number | string (optional) - Widthh: number | string (optional) - HeightminW: number | string (optional) - Minimum widthminH: number | string (optional) - Minimum heightmaxW: number | string (optional) - Maximum widthmaxH: number | string (optional) - Maximum heightoverflow: 'visible' | 'hidden' | 'scroll' (optional)opacity: number (optional)zIndex: number (optional)transform: ViewStyle['transform'] (optional)
Example usage:
import { Box } from '@shiplypalestine/ui';
// Basic Box
<Box
bg="blue.500"
p={4}
rounded="md"
>
<Text className="text-white">Basic Box</Text>
</Box>
// Box with Shadow
<Box
bg="white"
p={4}
rounded="md"
shadow={{
color: '#000',
offset: { width: 0, height: 2 },
opacity: 0.25,
radius: 3.84,
elevation: 5,
}}
>
<Text>Box with shadow</Text>
</Box>
// Box with Flex Layout
<Box
flexDirection="row"
justifyContent="space-between"
alignItems="center"
p={4}
bg="gray.100"
rounded="md"
>
<Box bg="blue.500" p={2} rounded="sm">
<Text className="text-white">Item 1</Text>
</Box>
<Box bg="green.500" p={2} rounded="sm">
<Text className="text-white">Item 2</Text>
</Box>
<Box bg="red.500" p={2} rounded="sm">
<Text className="text-white">Item 3</Text>
</Box>
</Box>
// Box with Position
<Box
position="absolute"
top={10}
right={10}
bg="purple.500"
p={4}
rounded="full"
>
<Text className="text-white">Positioned Box</Text>
</Box>
// Box with Transform
<Box
bg="yellow.500"
p={4}
rounded="md"
transform={[{ rotate: '45deg' }]}
>
<Text>Rotated Box</Text>
</Box>
// Box with Overflow
<Box
h={100}
w={200}
overflow="scroll"
bg="gray.100"
p={4}
rounded="md"
>
<Text>Scrollable content</Text>
</Box>CenterBox
A layout component that centers its children both horizontally and vertically.
import { CenterBox } from '@shiplypalestine/ui';
// Basic usage
<CenterBox>
<Text>Centered Content</Text>
</CenterBox>
// With flex
<CenterBox flex={1}>
<Text>Centered with flex</Text>
</CenterBox>
// Full-width and full-height
<CenterBox fullWidth fullHeight>
<Text>Centered in the entire container</Text>
</CenterBox>
// With NativeWind classes
<CenterBox className="bg-blue-100 p-4 rounded-lg">
<Text>Centered with NativeWind</Text>
</CenterBox>Props
style: Custom styles for the containerclassName: NativeWind classesflex: Flex value (default: 0)fullWidth: Whether to take full width (default: false)fullHeight: Whether to take full height (default: false)
Divider
A horizontal line component that can be used to separate content.
Props:
style: Custom styles for the DividerclassName: NativeWind classescolor: Line color (defaults to theme border color)thickness: Line thickness in pixels (default: 1)margin: Vertical margin in pixels (default: 8)
Label
A status-based label component that displays text with a colored background.
Props:
text: The text to display (required)status: The status type ('success' | 'error' | 'warning' | 'info')style: Custom styles for the containertextStyle: Custom styles for the textclassName: NativeWind classes for the containertextClassName: NativeWind classes for the text
RTL Support
The Shiply UI Library includes comprehensive support for right-to-left (RTL) languages such as Arabic, Hebrew, and Persian. This allows you to create applications that cater to global audiences with proper text and layout direction.
Setting Up RTL Support
The library provides an RTLProvider that automatically handles RTL detection based on the current language:
import { RTLProvider } from '@shiplypalestine/ui';
export default function App() {
return (
<RTLProvider>
{/* Your app content */}
</RTLProvider>
);
}Using the RTL Hooks and Components
RTL Hook
Use the useRTL hook to access RTL state and controls:
import { useRTL } from '@shiplypalestine/ui';
function MyComponent() {
const { isRTL, toggleRTL, setRTL } = useRTL();
return (
<View>
<Text>Current direction: {isRTL ? 'RTL' : 'LTR'}</Text>
<Button title="Toggle Direction" onPress={toggleRTL} />
<Button title="Set to RTL" onPress={() => setRTL(true)} />
<Button title="Set to LTR" onPress={() => setRTL(false)} />
</View>
);
}RTLView Component
Use the RTLView component to create containers that automatically handle text direction:
import { RTLView } from '@shiplypalestine/ui';
function MyComponent() {
return (
<RTLView>
<Text>This text will follow the current RTL setting</Text>
</RTLView>
);
}You can also force a specific direction regardless of the global setting:
<RTLView forceLTR={true}>
<Text>This will always be left-to-right</Text>
</RTLView>RTL-Aware Input Component
The Input component includes built-in RTL support with properties to control text direction:
import { Input } from '@shiplypalestine/ui';
function MyForm() {
return (
<View>
{/* Automatically follows the global RTL setting */}
<Input label="Username" placeholder="Enter username" />
{/* Force RTL regardless of global setting */}
<Input
label="الاسم"
placeholder="أدخل الاسم"
forceRTL={true}
/>
{/* Force LTR regardless of global setting */}
<Input
label="Email"
placeholder="Enter email"
forceLTR={true}
/>
</View>
);
}Language Selection
The library includes a LanguageSelector component that integrates with the RTL system:
import { LanguageSelector } from '@shiplypalestine/ui';
function SettingsScreen() {
return (
<View>
<LanguageSelector
onLanguageChange={(language) => {
console.log(`Language changed to ${language}`);
// The RTL direction will automatically update based on the language
}}
/>
</View>
);
}Best Practices for RTL Support
Use Relative Positioning: Always use
startandendinstead ofleftandrightin your styles.Avoid Hardcoded Directions: Don't hardcode text alignment or flex direction; use the RTL utilities.
Test with RTL Languages: Always test your UI with RTL languages to ensure proper alignment and visual formatting.
Accommodate Text Expansion: RTL languages often require more horizontal space, so design with flexibility.
Icons and Images: Make sure directional icons (arrows, back buttons) are flipped in RTL mode.
Tabs
The Tabs component provides a way to organize content into different views that can be switched between.
Basic Usage
import { Tabs } from '@shiplypalestine/ui';
function TabsExample() {
return (
<Tabs
tabs={[
{
key: 'tab1',
label: 'Tab 1',
content: <Text>Content for Tab 1</Text>,
},
{
key: 'tab2',
label: 'Tab 2',
content: <Text>Content for Tab 2</Text>,
},
{
key: 'tab3',
label: 'Tab 3',
content: <Text>Content for Tab 3</Text>,
},
]}
onTabChange={(tabKey) => console.log(`Tab changed to ${tabKey}`)}
/>
);
}Tabs with Icons
import { Tabs } from '@shiplypalestine/ui';
import { HomeIcon, ProfileIcon, SettingsIcon } from './icons';
function TabsWithIconsExample() {
return (
<Tabs
tabs={[
{
key: 'home',
label: 'Home',
icon: <HomeIcon />,
content: <Text>Home content</Text>,
},
{
key: 'profile',
label: 'Profile',
icon: <ProfileIcon />,
content: <Text>Profile content</Text>,
},
{
key: 'settings',
label: 'Settings',
icon: <SettingsIcon />,
content: <Text>Settings content</Text>,
},
]}
/>
);
}Custom Styled Tabs
import { Tabs } from '@shiplypalestine/ui';
function StyledTabsExample() {
return (
<Tabs
tabs={[
{
key: 'photos',
label: 'Photos',
content: <Text>Photos content</Text>,
},
{
key: 'videos',
label: 'Videos',
content: <Text>Videos content</Text>,
},
{
key: 'files',
label: 'Files',
content: <Text>Files content</Text>,
},
]}
tabsContainerClassName="bg-gray-100 dark:bg-gray-800 rounded-t-lg"
tabButtonClassName="flex-1"
activeTabButtonClassName="bg-white dark:bg-gray-700"
contentContainerClassName="bg-white dark:bg-gray-700 rounded-b-lg"
/>
);
}Props
Tabs Component Props
| Prop | Type | Description |
|------|------|-------------|
| tabs | TabItemProps[] | Array of tab items |
| initialTabKey | string | Key of the initially active tab |
| containerStyle | ViewStyle | Style for the main container |
| tabsContainerStyle | ViewStyle | Style for the tabs container |
| tabButtonStyle | ViewStyle | Style for the tab buttons |
| activeTabButtonStyle | ViewStyle | Style for the active tab button |
| tabTextStyle | TextStyle | Style for the tab text |
| activeTabTextStyle | TextStyle | Style for the active tab text |
| contentContainerStyle | ViewStyle | Style for the content container |
| containerClassName | string | NativeWind class for the main container |
| tabsContainerClassName | string | NativeWind class for the tabs container |
| tabButtonClassName | string | NativeWind class for the tab buttons |
| activeTabButtonClassName | string | NativeWind class for the active tab button |
| tabTextClassName | string | NativeWind class for the tab text |
| activeTabTextClassName | string | NativeWind class for the active tab text |
| contentContainerClassName | string | NativeWind class for the content container |
| onTabChange | (tabKey: string) => void | Callback fired when a tab is selected |
TabItemProps Interface
| Property | Type | Description |
|----------|------|-------------|
| key | string | Unique identifier for the tab |
| label | string | Display label for the tab |
| icon | ReactNode | Optional icon for the tab |
| content | ReactNode | Content to be displayed when tab is active |
PhoneNumber
A component for international phone number input that combines country code selection with a phone number input field.
Props:
value: string (required) - The current full phone number valueonChangeText: (value: string) => void (required) - Callback for when the phone number changescountryCodes: CountryCode[] (required) - Array of country codes to select frominitialCountryCode: string (optional) - Initial country code to selectlabel: string (optional) - Label for the inputerror: string (optional) - Error messageplaceholder: string (optional) - Placeholder for the phone number inputrequired: boolean (optional) - Whether the field is requireddisabled: boolean (optional) - Whether the field is disabledcontainerStyle: ViewStyle (optional) - Style for the containerinputStyle: TextStyle (optional) - Style for the inputlabelStyle: TextStyle (optional) - Style for the labelerrorStyle: TextStyle (optional) - Style for the error messagedropdownStyle: ViewStyle (optional) - Style for the dropdowncountryCodeStyle: TextStyle (optional) - Style for the country code textcountryCodeContainerStyle: ViewStyle (optional) - Style for the country code containercontainerClassName: string (optional) - Class for the containerinputClassName: string (optional) - Class for the inputlabelClassName: string (optional) - Class for the labelerrorClassName: string (optional) - Class for the error messagedropdownClassName: string (optional) - Class for the dropdowncountryCodeClassName: string (optional) - Class for the country code textcountryCodeContainerClassName: string (optional) - Class for the country code containerforceRTL: boolean (optional) - Force right-to-left text directionforceLTR: boolean (optional) - Force left-to-right text direction
Example usage:
import { PhoneNumber, CountryCode } from '@shiplypalestine/ui';
// Define country codes
const countryCodes: CountryCode[] = [
{ code: '+1', name: 'United States', flag: '🇺🇸' },
{ code: '+44', name: 'United Kingdom', flag: '🇬🇧' },
{ code: '+33', name: 'France', flag: '🇫🇷' },
// Add more country codes...
];
// Basic usage
function PhoneNumberExample() {
const [phoneNumber, setPhoneNumber] = useState('');
return (
<PhoneNumber
value={phoneNumber}
onChangeText={setPhoneNumber}
countryCodes={countryCodes}
label="Phone Number"
placeholder="Enter phone number"
/>
);
}
// With initial country code
<PhoneNumber
value={phoneNumber}
onChangeText={setPhoneNumber}
countryCodes={countryCodes}
initialCountryCode="+44"
label="UK Phone Number"
placeholder="Enter UK phone number"
/>
// With error state
<PhoneNumber
value={phoneNumber}
onChangeText={setPhoneNumber}
countryCodes={countryCodes}
label="Phone Number"
error="Invalid phone number"
/>
// With RTL support
<PhoneNumber
value={phoneNumber}
onChangeText={setPhoneNumber}
countryCodes={countryCodes}
label="رقم الهاتف"
placeholder="أدخل رقم الهاتف"
forceRTL={true}
/>
// Required field
<PhoneNumber
value={phoneNumber}
onChangeText={setPhoneNumber}
countryCodes={countryCodes}
label="Phone Number"
required={true}
/>CountryCode Interface
interface CountryCode {
code: string; // The country dial code, e.g., '+1'
name: string; // The country name, e.g., 'United States'
flag?: string; // Optional emoji flag, e.g., '🇺🇸'
}Autocomplete
A high-performance autocomplete component that supports large datasets, API integration, and customizable styling.
Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| value | string | Yes | The current input value |
| onChangeText | (text: string) => void | Yes | Callback when the input text changes |
| onSelect | (item: AutocompleteItem) => void | Yes | Callback when an item is selected |
| fetchData | (query: string) => Promise<AutocompleteItem[]> | Yes | Function to fetch data based on the query |
| label | string | No | Label text for the input |
| placeholder | string | No | Placeholder text for the input |
| error | string | No | Error message to display |
| required | boolean | No | Whether the field is required |
| disabled | boolean | No | Whether the input is disabled |
| forceRTL | boolean | No | Force RTL layout |
| containerClassName | string | No | Custom class for the container |
| inputClassName | string | No | Custom class for the input |
| listClassName | string | No | Custom class for the results list |
| itemClassName | string | No | Custom class for list items |
AutocompleteItem Interface
interface AutocompleteItem {
id: string;
label: string;
value: string;
}Example Usage
import { Autocomplete } from 'shiply-ui';
const MyComponent = () => {
const [value, setValue] = useState('');
const [selectedItem, setSelectedItem] = useState<AutocompleteItem | null>(null);
const fetchData = async (query: string): Promise<AutocompleteItem[]> => {
// Your API call here
const response = await fetch(`/api/search?q=${query}`);
return response.json();
};
return (
<Autocomplete
value={value}
onChangeText={setValue}
onSelect={setSelectedItem}
fetchData={fetchData}
label="Search"
placeholder="Type to search..."
/>
);
};Features
- Debounced search to prevent excessive API calls
- Virtualized list for handling large datasets
- Loading states and error handling
- Keyboard navigation support
- RTL support
- Customizable styling
- Clear button with loading indicator
ActionSheet
A bottom sheet component that slides up from the bottom of the screen, commonly used for presenting a set of actions or options. Supports both web and mobile platforms with proper positioning and z-index handling.
import { ActionSheet } from 'shiply-ui';
// Basic usage
<ActionSheet
visible={isVisible}
onClose={() => setIsVisible(false)}
title="Options"
>
<View>
<Text>Content goes here</Text>
</View>
</ActionSheet>
// With custom styles
<ActionSheet
visible={isVisible}
onClose={() => setIsVisible(false)}
title="Custom Styled Options"
containerStyle={{ backgroundColor: 'rgba(0, 0, 0, 0.7)' }}
contentStyle={{
backgroundColor: '#F7FAFC',
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
}}
titleStyle={{
color: '#2D3748',
fontSize: 20,
fontWeight: 'bold',
}}
searchContainerStyle={{
backgroundColor: '#EDF2F7',
borderRadius: 8,
margin: 16,
}}
searchInputStyle={{
backgroundColor: '#FFFFFF',
borderRadius: 6,
}}
>
<View>
<Text>Custom styled content</Text>
</View>
</ActionSheet>
// With default search
<ActionSheet
visible={isVisible}
onClose={() => setIsVisible(false)}
title="Search Options"
showSearch
searchPlaceholder="Search items..."
onSearch={(query) => {
console.log('Searching for:', query);
}}
searchValue={searchQuery}
isLoading={isLoading}
searchInputProps={{
autoCapitalize: 'none',
autoCorrect: false,
}}
>
<View>
<Text>Search results go here</Text>
</View>
</ActionSheet>
// With custom search input
<ActionSheet
visible={isVisible}
onClose={() => setIsVisible(false)}
title="Custom Search"
showSearch
searchInputComponent={
<Input
placeholder="Search with custom input..."
value={searchQuery}
onChangeText={setSearchQuery}
placeholderType="search"
startComponent={
<View style={{ width: 20, height: 20, backgroundColor: 'blue', borderRadius: 10 }} />
}
/>
}
isLoading={isLoading}
>
<View>
<Text>Search results go here</Text>
</View>
</ActionSheet>Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| visible | boolean | false | Controls the visibility of the action sheet |
| onClose | () => void | - | Callback function called when the action sheet is closed |
| title | string | - | Optional title displayed at the top of the action sheet |
| children | React.ReactNode | - | Content to be displayed in the action sheet |
| showSearch | boolean | false | Whether to show the search input |
| searchPlaceholder | string | 'Search...' | Placeholder text for the search input |
| onSearch | (query: string) => void | - | Callback function called when the search query changes |
| searchValue | string | '' | Current value of the search input |
| isLoading | boolean | false | Whether to show a loading indicator |
| searchInputProps | TextInputProps | - | Additional props to pass to the default search input |
| searchInputComponent | React.ReactNode | - | Custom search input component to use instead of the default |
| emptyMessage | string | 'No items found' | Message to display when no content is provided |
| minHeight | number | 500 | Minimum height of the action sheet |
Style Props
| Prop | Type | Description |
|------|------|-------------|
| style | ViewStyle | Custom style for the content container |
| className | string | NativeWind classes for the content container |
| containerStyle | ViewStyle | Custom style for the main container |
| containerClassName | string | NativeWind classes for the main container |
| contentStyle | ViewStyle | Custom style for the action sheet content |
| contentClassName | string | NativeWind classes for the action sheet content |
| overlayStyle | ViewStyle | Custom style for the overlay background |
| overlayClassName | string | NativeWind classes for the overlay background |
| titleStyle | TextStyle | Custom style for the title text |
| titleClassName | string | NativeWind classes for the title text |
| searchContainerStyle | ViewStyle | Custom style for the search container |
| searchContainerClassName | string | NativeWind classes for the search container |
| searchInputStyle | TextStyle | Custom style for the search input |
| searchInputClassName | string | NativeWind classes for the search input |
Platform Support
- Web: Uses
position: fixedwith proper z-index for overlay positioning - Mobile: Uses React Native Modal with slide-up animation
- Cross-platform: Consistent API and behavior across platforms
Overlay
A flexible overlay component that creates a backdrop and can wrap other components like spinners, modals, or notifications. Supports multiple positions and customizable styling.
import { Overlay } from '@shiplypalestine/ui';
// Basic overlay with centered content
<Overlay
isOpen={isVisible}
onClose={() => setIsVisible(false)}
position="center"
>
<View style={{ backgroundColor: 'white', padding: 20, borderRadius: 8 }}>
<Text>Overlay Content</Text>
</View>
</Overlay>
// Overlay with spinner for loading states
<Overlay
isOpen={isLoading}
position="center"
dismissible={false}
closeOnBackdropPress={false}
>
<View style={{ backgroundColor: 'white', padding: 32, borderRadius: 16, alignItems: 'center' }}>
<Spinner size="large" color="primary" />
<Text style={{ marginTop: 16 }}>Loading...</Text>
</View>
</Overlay>
// Top position notification
<Overlay
isOpen={showNotification}
onClose={() => setShowNotification(false)}
position="top"
backdropOpacity={0.4}
>
<View style={{ backgroundColor: 'white', padding: 16, borderRadius: 8 }}>
<Text>Top notification message</Text>
</View>
</Overlay>
// Bottom position with custom styling and unmountOnExit
<Overlay
isOpen={isVisible}
onClose={() => setIsVisible(false)}
position="bottom"
backdropColor="rgba(0, 0, 0, 0.3)"
contentStyle={{ marginBottom: 20 }}
unmountOnExit={true}
>
<View style={{ backgroundColor: 'white', padding: 20, borderRadius: 12 }}>
<Text>Bottom positioned content</Text>
</View>
</Overlay>Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| isOpen | boolean | false | Controls the visibility of the overlay |
| onClose | () => void | - | Callback function called when the overlay is closed |
| children | React.ReactNode | - | Content to be displayed in the overlay |
| dismissible | boolean | true | Whether the overlay can be dismissed |
| closeOnBackdropPress | boolean | true | Whether tapping the backdrop closes the overlay |
| unmountOnExit | boolean | false | Whether to unmount the component when closed |
| animationType | 'fade' \| 'slide' \| 'none' | 'fade' | Animation type for mobile modal |
| position | 'center' \| 'top' \| 'bottom' \| 'left' \| 'right' | 'center' | Position of the content within the overlay |
| backdropColor | string | 'rgba(0, 0, 0, 0.5)' | Color of the backdrop |
| backdropOpacity | number | 0.5 | Opacity of the backdrop |
| zIndex | number | 9999 | Z-index for web positioning |
Style Props
| Prop | Type | Description |
|------|------|-------------|
| style | ViewStyle | Custom style for the content container |
| className | string | NativeWind classes for the content container |
| containerStyle | ViewStyle | Custom style for the main container |
| containerClassName | string | NativeWind classes for the main container |
| backdropStyle | ViewStyle | Custom style for the backdrop |
| backdropClassName | string | NativeWind classes for the backdrop |
| contentStyle | ViewStyle | Custom style for the content wrapper |
| contentClassName | string | NativeWind classes for the content wrapper |
Position Options
center: Content is centered both horizontally and verticallytop: Content appears at the top of the screen with marginbottom: Content appears at the bottom of the screen with marginleft: Content appears at the left side of the screen with marginright: Content appears at the right side of the screen with margin
Use Cases
- Loading States: Wrap a Spinner component for loading overlays
- Notifications: Use top/bottom positions for toast-like notifications
- Modals: Create custom modal dialogs with centered content
- Full-screen Overlays: Use for complex overlays that need full screen coverage
- Side Panels: Use left/right positions for slide-out panels
Platform Support
- Web: Uses
position: fixedwith proper z-index for overlay positioning - Mobile: Uses React Native Modal with customizable animation types
- Cross-platform: Consistent API and behavior across platforms
Skeleton
A flexible skeleton placeholder component for loading states. Supports shimmer animation, shape variants, and full style customization.
Note: You must install react-native-linear-gradient in your project for shimmer animation to work:
npm install react-native-linear-gradient
# or
yarn add react-native-linear-gradientUsage
import { Skeleton } from '@shiplypalestine/ui';
// Rectangle skeleton (default)
<Skeleton width={120} height={16} />
// Rounded skeleton
<Skeleton width={120} height={16} variant="rounded" />
// Circle skeleton (e.g. avatar)
<Skeleton width={40} height={40} variant="circle" />
// Custom border radius
<Skeleton width={100} height={20} borderRadius={8} />
// No animation
<Skeleton width={120} height={16} animation="none" />
// Full width skeleton (shimmer will be disabled if width is not a number)
<Skeleton width="100%" height={16} />Props
| Prop | Type | Default | Description | |--------------|-------------------------------|-------------|--------------------------------------------------| | width | number | string | '100%' | Width of the skeleton | | height | number | string | 16 | Height of the skeleton | | borderRadius | number | - | Border radius (overrides variant) | | variant | 'rect' | 'circle' | 'rounded'| 'rect' | Shape of the skeleton | | style | ViewStyle | - | Custom style for the container | | className | string | - | NativeWind classes for the container | | animation | 'shimmer' | 'none' | 'shimmer' | Animation type |
- If
variantiscircle, the skeleton will be perfectly round. - If
variantisrounded, the skeleton will have a default border radius of 12 unless overridden. - If
widthis not a number, shimmer animation will be disabled and a warning will be shown in development.
Development
- Clone the repository
- Install dependencies:
npm install - Start the development server: `
### Link
A customizable link component for navigation or actions. Supports custom styles and classes for both the container and text.
#### Usage
```tsx
import { Link } from '@shiplypalestine/ui';
// Basic usage
<Link onPress={() => {}}>Default Link</Link>
// With custom styles
<Link
onPress={() => {}}
style={{ margin: 8 }}
textStyle={{ color: 'red', fontWeight: 'bold' }}
>
Custom Styled Link
</Link>
// With NativeWind/Tailwind classes
<Link
onPress={() => {}}
className="rounded"
textClassName="text-lg underline text-green-600"
>
Tailwind Link
</Link>Props
| Prop | Type | Default | Description | |----------------|-----------------------------|-------------|---------------------------------------------| | onPress | () => void | required | Function to call when link is pressed | | children | React.ReactNode | required | Link content | | variant | 'default' | 'primary' | 'secondary' | 'default' | Color variant | | disabled | boolean | false | Disable the link | | underline | boolean | false | Underline the link text | | size | 'small' | 'medium' | 'large' | 'medium' | Text size | | style | ViewStyle | - | Custom style for the container | | textStyle | TextStyle | - | Custom style for the text | | className | string | - | NativeWind classes for the container | | textClassName | string | - | NativeWind classes for the text |
Icon Component
A flexible icon wrapper for any icon library (e.g., Feather, MaterialIcons, Ionicons, etc.).
Props
| Prop | Type | Required | Description | |--------|--------|----------|-------------| | as | React.ReactNode | Yes | The icon element (e.g., ) | | style | object | No | Custom style for the wrapper | | className | string | No | NativeWind/Tailwind classes for the wrapper | | ...rest| any | No | Any other props for the wrapper |
Usage
import { Icon } from '@shiplypalestine/ui';
import { Feather, MaterialIcons, Ionicons } from '@expo/vector-icons';
// Feather icon
<Icon as={<Feather name="home" color="#2563EB" size={32} />} />
// MaterialIcons icon
<Icon as={<MaterialIcons name="favorite" color="#EF4444" size={32} />} />
// Ionicons icon
<Icon as={<Ionicons name="receipt" size={32} color="#2563EB" />} />
// With custom style and Tailwind/NativeWind classes
<Icon as={<Feather name="user" color="#22C55E" size={32} />} className="rounded-full bg-gray-100 p-2" style={{ margin: 8 }} />- The
asprop is the icon element itself (not a component type or string). - You can use any icon component from libraries like
@expo/vector-iconsorreact-native-vector-icons. - All other props are applied to the wrapper
<View>.
Image Component
A flexible image component that supports fallback alt text, size, and NativeWind/Tailwind classes.
Props
| Prop | Type | Required | Description | |-----------|---------------------|----------|-------------| | size | number | No | Sets both width and height of the image | | alt | string | No | Fallback text to show if image fails to load | | source | object | Yes | The image source (same as React Native Image) | | className | string | No | NativeWind/Tailwind classes for styling | | style | object | No | Custom style | | ...rest | any | No | Any other native Image props |
Usage
import { Image } from '@shiplypalestine/ui';
// Basic usage
<Image
size={150}
alt="fallback text"
source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
/>
// With fallback/alt text
<Image
size={100}
alt="Logo not found"
source={{ uri: 'https://invalid-url-to-trigger-fallback.png' }}
/>
// With Tailwind/NativeWind classes
<Image
size={60}
alt="Avatar"
source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
className="rounded-full border border-blue-500"
/>- If the image fails to load, the
alttext will be shown in a styled fallback box. - The
sizeprop sets both width and height. You can also usestyleorclassNamefor more control. - All native Image props are supported.
