Essential constants, styles and hooks for react native.
npm install @shaquillehinds/react-native-essentialsComplete API reference for React Native Essentials.
- Constants
- Components
- Layouts
- Typography
- Buttons
- Inputs
- Providers
- Wrappers
- Indicators
- Injectors
- Hooks
- Utilities
- Animations
- Gestures
- Styles
- Algorithms
---
Access device dimensions and platform information.
#### Exports
``tsx`
import {
SCREEN_WIDTH,
SCREEN_HEIGHT,
WINDOW_WIDTH,
WINDOW_HEIGHT,
initialOrientation,
aspectRatio,
isSmallDevice,
isLargeDevice,
isIOS,
isWeb,
isTablet,
isIpad,
isAndroid,
type DeviceOrientation,
type ScaledSizeDimensions,
} from '@shaquillehinds/react-native-essentials';
#### Values
- SCREEN_WIDTH: number - Full screen widthSCREEN_HEIGHT: number
- - Full screen heightWINDOW_WIDTH: number
- - Window width (excludes system UI)WINDOW_HEIGHT: number
- - Window height (excludes system UI)initialOrientation: DeviceOrientation
- - Initial device orientation ('portrait' | 'landscape')aspectRatio: number
- - Screen aspect ratioisSmallDevice: boolean
- - True if screen is smaller than 375x750isLargeDevice: boolean
- - True if screen width > 1100isIOS: boolean | undefined
- - True if iOS platformisWeb: boolean | undefined
- - True if web platformisTablet: boolean
- - True if aspect ratio <= 1.6isIpad: boolean
- - True if iOS tabletisAndroid: boolean | undefined
- - True if Android platform
---
Flexible layout components with responsive spacing and styling.
#### Layout
Versatile container component with extensive layout options.
> 💡 Important: When passing numbers to width and height props, they are automatically treated as percentages of screen dimensions. width={80} means 80% of screen width, height={50} means 50% of screen height. The component internally applies relativeX() and relativeY() conversions. You can also pass string values like '100%' or '50px' for standard React Native sizing.
`tsx
import { Layout } from '@shaquillehinds/react-native-essentials';
padding={[5]}
backgroundColor="#ffffff"
width={90} // 90% of screen width (converted internally)
height={50} // 50% of screen height (converted internally)
>
{children}
;
`
Props:
`tsx
interface LayoutProps
// Spacing
margin?: Spacing;
padding?: Spacing;
// Dimensions (percentages of screen or DimensionValue)
// IMPORTANT: Numbers are automatically converted to percentages
// width={80} → 80% of screen width (relativeX applied internally)
// height={50} → 50% of screen height (relativeY applied internally)
width?: DimensionValue;
height?: DimensionValue;
// Flex
flex?: [number] | [number, number] | [number, number, DimensionValue];
flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse';
wrap?: boolean;
// Alignment
center?: boolean; // Center both axes
centerX?: boolean; // Center horizontally
alignSelf?: FlexAlignType;
spaceEnd?: boolean;
spaceStart?: boolean;
spaceEven?: boolean;
spaceCenter?: boolean;
spaceBetween?: boolean;
// Position
absolute?: boolean;
top?: DimensionValue;
bottom?: DimensionValue;
left?: DimensionValue;
right?: DimensionValue;
// Appearance
backgroundColor?: string;
// States
loading?: LoadingIndicatorProps | boolean;
skeleton?: { colors?: string[] } | boolean;
// Scrolling
scrollable?: Scrollable;
// Standard props
style?: StyleProp
children?: ReactNode;
}
`
Spacing Type:
`tsx
type Spaces = [number, number?, number?, number?];
type Spacing = {
/**
* This padding/margin shorthand follows the classic CSS shorthand.
* Numbers are used as percentages of screen width or height.
* - [10] = {top: 10%, right: 10%, bottom: 10%, left: 10%}
* - [10, 20] = {top: 10%, right: 20%, bottom: 10%, left: 20%}
* - [10, 20, 30] = {top: 10%, right: 20%, bottom: 30%, left: 20%}
* - [10, 20, 30, 40] = {top: 10%, right: 20%, bottom: 30%, left: 40%}
*/
padding?: Spaces;
margin?: Spaces;
};
`
#### RowLayout
Horizontal layout component (shorthand for flexDirection='row').
`tsx
import { RowLayout } from '@shaquillehinds/react-native-essentials';
`
#### ScreenLayout
Full-screen container with safe area support.
`tsx
import { ScreenLayout } from '@shaquillehinds/react-native-essentials';
`
Props:
`tsx`
interface ScreenLayoutProps extends LayoutProps {
// Inherits all Layout props
}
#### AnimatedLayout
Layout component using Reanimated's Animated.View and Animated.ScrollView instead of standard React Native views. Identical props to Layout, but renders with Reanimated components for better animation performance.
`tsx
import { AnimatedLayout } from '@shaquillehinds/react-native-essentials';
import { useSharedValue, useAnimatedStyle } from 'react-native-reanimated';
function Component() {
const opacity = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => ({
opacity: opacity.value,
}));
return (
{children}
);
}
`
Props:
`tsx`
interface AnimatedLayoutProps extends LayoutProps {
// Identical to LayoutProps - uses Reanimated components internally
}
---
Text components with consistent styling.
#### BaseText
Foundation text component with font management.
`tsx
import { BaseText } from '@shaquillehinds/react-native-essentials';
Text Content
`
Props:
`tsx
interface BaseTextProps extends TextProps {
fontSize?: FontSize;
fontStyle?: FontStyle;
customColor?: string;
animate?: boolean;
animatedStyle?: AnimatedStyleProp
children?: ReactNode;
}
type FontSize =
| 'headingL'
| 'headingM'
| 'headingS'
| 'titleL'
| 'titleM'
| 'titleS'
| 'bodyL'
| 'bodyM'
| 'bodyS';
type FontStyle =
| 'Thin'
| 'Extra Light'
| 'Light'
| 'Regular'
| 'Medium'
| 'SemiBold'
| 'Bold'
| 'ExtraBold'
| 'Black';
`
#### Title
Large title text component.
`tsx
import { Title } from '@shaquillehinds/react-native-essentials';
#### Heading
Section heading component.
`tsx
import { Heading } from '@shaquillehinds/react-native-essentials';Section Heading ;
`#### Body
Body text component.
`tsx
import { Body } from '@shaquillehinds/react-native-essentials';Body text content;
`#### Translate
Auto-translating text component (requires LocalizationProvider).
`tsx
import { Translate } from '@shaquillehinds/react-native-essentials';Hello World ;
`---
$3
#### BaseButton
Customizable button component with loading states and animations.
`tsx
import { BaseButton } from '@shaquillehinds/react-native-essentials'; buttonSize="large"
backgroundColor="#007AFF"
borderRadius="curvy"
loading={false}
disabled={false}
onPress={() => console.log('Pressed')}
leftComponent={ }
rightComponent={ }
>
Button Text
;
`Props:
`tsx
interface ButtonProps extends Omit {
// Size
buttonSize?: 'small' | 'medium' | 'large' | 'auto' | 'wide'; // Spacing (uses Spacing type with array syntax)
margin?: Spaces; // e.g., [5], [5, 10], [5, 10, 5, 10]
padding?: Spaces; // e.g., [5], [5, 10], [5, 10, 5, 10]
// Border
borderColor?: string;
borderWidth?: 'razor' | 'thin' | 'medium' | 'large';
borderRadius?: 'edgy' | 'sharp' | 'medium' | 'soft' | 'curvy' | 'round';
// Appearance
backgroundColor?: string;
customFontColor?: string;
alignSelf?: FlexAlignType;
// Behavior
onPress?: () => void;
disabled?: boolean;
loading?: LoadingIndicatorProps | boolean;
activeOpacity?: number;
// Components
leftComponent?: ReactNode;
rightComponent?: ReactNode;
// Animation
animate?: boolean;
// Style
style?: StyleProp>>;
textStyle?: StyleProp>>;
fontStyle?: FontStyle;
children?: ReactNode;
}
`---
$3
#### BaseInput
Foundation input component with flexible rendering options and ref management.
`tsx
import { BaseInput } from '@shaquillehinds/react-native-essentials';
import { useRef } from 'react';function Component() {
// Option 1: Use standard TextInput ref
const inputRef = useRef(null);
return (
backgroundColor="#ffffff"
focusedBorderColor="#007AFF"
blurredBorderColor="#cccccc"
textInputProps={{
placeholder: 'Enter text',
onChangeText: (text) => console.log(text),
}}
refTextInput={inputRef}
LeftComponent={ }
RightComponent={ }
padding={[3]}
/>
);
}
`Props:
`tsx
interface BaseInputProps extends LayoutProps {
backgroundColor: string;
focusedBorderColor?: string;
blurredBorderColor?: string;
textInputProps: TextInputProps;
LeftComponent?: React.ReactNode;
RightComponent?: React.ReactNode;
TextInputComponent?: (
props: TextInputProps & {
ref?: React.RefObject;
}
) => React.JSX.Element;
refTextInput?: React.MutableRefObject;
refStateInput?: React.Ref;
refStateInputValidator?: (text: string) => boolean;
// Inherits all LayoutProps including margin and padding
}
`#### StateTextInput
Input component with isolated state management. Changes to the input don't trigger parent re-renders. The special
refStateInput ref allows you to read and update the value imperatively from the parent without causing re-renders.`tsx
import {
BaseInput,
StateTextInput,
} from '@shaquillehinds/react-native-essentials';
import { useRef } from 'react';function OptimizedForm() {
// StateInputRef provides access to value without re-rendering parent
const emailRef = useRef(null);
const passwordRef = useRef(null);
const handleSubmit = () => {
// Access values without triggering re-renders during typing
const email = emailRef.current?.value;
const password = passwordRef.current?.value;
console.log({ email, password });
};
const handleReset = () => {
// Imperatively set values without parent re-render
emailRef.current?.setValue('');
passwordRef.current?.setValue('');
};
return (
<>
backgroundColor="#ffffff"
textInputProps={{
placeholder: 'Email',
keyboardType: 'email-address',
}}
refStateInput={emailRef}
refStateInputValidator={(text) => text.length <= 100}
padding={[3]}
/>
backgroundColor="#ffffff"
textInputProps={{
placeholder: 'Password',
secureTextEntry: true,
}}
refStateInput={passwordRef}
padding={[3]}
/>
>
);
}
// Or use StateTextInput directly
function DirectUsage() {
const inputRef = useRef(null);
return (
placeholder="Direct usage"
refStateInput={inputRef}
refStateInputValidator={(text) => /^[a-zA-Z]*$/.test(text)}
onChangeText={(text) => console.log('Valid text:', text)}
/>
);
}
`StateInputRef Type:
`tsx
type StateInputRef = {
value: string;
setValue: React.Dispatch>;
};
`Benefits:
- Performance: Parent component doesn't re-render on every keystroke
- Imperative Access: Read/write input value without state updates
- Validation: Built-in validator prevents invalid input from being set
- Form Optimization: Perfect for forms with many inputs
---
$3
#### PortalProvider
Manages portal/overlay content.
`tsx
import {
PortalProvider,
usePortal,
} from '@shaquillehinds/react-native-essentials';function App() {
return (
);
}
function Component() {
const portal = usePortal();
const showModal = () => {
const key = portal.mount(
'modal-key',
portal.unmount('modal-key')} />
);
};
return ;
}
`Context Value:
`tsx
interface PortalContextValue {
mount: (
key: PortalKey,
element: ReactNode,
onMount?: (key: PortalKey) => void
) => PortalKey;
update: (key: PortalKey, element: ReactNode) => void;
unmount: (key: PortalKey, onUnmount?: (key: PortalKey) => void) => void;
}
`#### LocalizationProvider
Provides app-wide translation support.
`tsx
import {
LocalizationProvider,
useLocalization,
} from '@shaquillehinds/react-native-essentials'; sourceLanguage="en-US"
targetLanguage="es-ES"
translation={async ({ sourceLanguage, targetLanguage, text }) => {
// Your translation API call
return translatedText;
}}
initialLanguagesRecord={{
'es-ES': {
base64hash: 'translated text',
},
}}
initialLanguagesRecordRetriever={async ({
sourceLanguage,
targetLanguage,
}) => {
// Fetch saved translations
return [{ original: 'Hello', translated: 'Hola' }];
}}
>
;
`Props:
`tsx
interface LocalizationProviderProps {
children: ReactNode;
sourceLanguage: LanguageCode;
targetLanguage: LanguageCode;
translation: (props: {
sourceLanguage: LanguageCode;
targetLanguage: LanguageCode;
text: string;
}) => Promise;
initialLanguagesRecord?: LanguagesRecord;
initialLanguagesRecordRetriever?: (props: {
sourceLanguage: LanguageCode;
targetLanguage: LanguageCode;
}) => Promise<{ original: string; translated: string }[]>;
}
`#### EventTrackersProvider
Centralized event tracking system.
`tsx
import {
EventTrackersProvider,
useEventTrackers,
} from '@shaquillehinds/react-native-essentials'; eventTrackers={[
{
name: 'Analytics',
trackEvent: (eventName, properties) => {
analytics.track(eventName, properties);
},
},
]}
>
;
`#### DataCollectionProvider
Manages centralized data collection.
`tsx
import {
DataCollectionProvider,
useDataCollection,
} from '@shaquillehinds/react-native-essentials';
;
`---
$3
#### Press
Enhanced pressable component.
`tsx
import { Press } from '@shaquillehinds/react-native-essentials'; onPress={() => console.log('Pressed')}
activeOpacity={0.7}
disabled={false}
>
{children}
;
`#### PressableLayout
Combines Layout and Press functionality - a pressable Layout component with all Layout props.
`tsx
import { PressableLayout } from '@shaquillehinds/react-native-essentials'; onPress={() => console.log('Pressed')}
center
padding={[5]}
backgroundColor="#f0f0f0"
activeOpacity={0.7}
>
{children}
;
`Props:
`tsx
interface PressableLayoutProps extends LayoutProps {
onPress?: () => void;
activeOpacity?: number;
disabled?: boolean;
// Inherits all Layout props
}
`#### TextStream
Animated text streaming component.
`tsx
import { TextStream } from '@shaquillehinds/react-native-essentials'; text="Text to stream"
speed={50}
onComplete={() => console.log('Done')}
/>;
`---
$3
#### LoadingIndicator
Displays loading state with spinner.
`tsx
import { LoadingIndicator } from '@shaquillehinds/react-native-essentials'; ;
`#### SkeletonViewIndicator
Skeleton loading placeholder.
`tsx
import { SkeletonViewIndicator } from '@shaquillehinds/react-native-essentials'; ;
`---
$3
#### ViewDimensionsInjector
Injects view dimensions into children.
`tsx
import { ViewDimensionsInjector } from '@shaquillehinds/react-native-essentials';
{({ width, height }) => (
{/ Content /}
)}
;
`---
Hooks
$3
Hook for responsive device orientation and dimensions.
`tsx
import { useDeviceOrientation } from '@shaquillehinds/react-native-essentials';function Component() {
const orientation = useDeviceOrientation();
return (
Orientation: {orientation.isPortrait ? 'Portrait' : 'Landscape'}
);
}
`Returns:
`tsx
{
orientation: 'portrait' | 'landscape';
isPortrait: boolean;
isLandscape: boolean;
screenWidth: number;
screenHeight: number;
windowWidth: number;
windowHeight: number;
relativeX: (percentage: number) => number;
relativeY: (percentage: number) => number;
relativeShort: (percentage: number) => number;
relativeLong: (percentage: number) => number;
}
`$3
Calculate responsive image dimensions.
`tsx
import { useImageSize } from '@shaquillehinds/react-native-essentials';function ImageComponent() {
const { imageSize } = useImageSize({
image: 'https://example.com/image.jpg',
maxWidth: 300,
maxHeight: 400,
});
return (
source={{ uri: 'https://example.com/image.jpg' }}
style={imageSize}
/>
);
}
`Props:
`tsx
interface UseImageSizeProps {
image?: string;
maxWidth: number;
maxHeight: number;
}
`Returns:
`tsx
{
imageSize: {
width: number;
height: number;
}
calculateSize: (props: {
imageWidth: number;
imageHeight: number;
maxWidth: number;
maxHeight: number;
}) => {
displayWidth: number;
displayHeight: number;
};
}
`$3
Calculate dimensions for multiple images.
`tsx
import { useImageSizes } from '@shaquillehinds/react-native-essentials';const { imageSizes } = useImageSizes({
images: ['url1', 'url2', 'url3'],
maxWidth: 300,
maxHeight: 400,
});
`$3
Countdown timer hook.
`tsx
import { useTimer } from '@shaquillehinds/react-native-essentials';function Timer() {
const { seconds, minutes } = useTimer({
seconds: 120,
start: true,
onTimerEnds: () => console.log('Timer ended'),
});
return (
{minutes}:{seconds}
);
}
`Props:
`tsx
interface UseTimerProps {
seconds: number;
onTimerEnds: () => void;
start: boolean;
}
`$3
Debounce function calls with optional per-call overrides.
`tsx
import { useDebounce } from '@shaquillehinds/react-native-essentials';function SearchInput() {
const [query, setQuery] = useState('');
const { debounce, cancelDebounce } = useDebounce({
delayInMilliSecs: 500,
onTrigger: async () => {
// Perform search with current query
const results = await searchAPI(query);
setResults(results);
},
onPreTrigger: () => console.log('About to search'),
onPostTrigger: () => console.log('Search complete'),
onDebounceInvocation: () => console.log('User is typing'),
});
return (
value={query}
onChangeText={(text) => {
setQuery(text);
debounce(); // Uses props from hook initialization
}}
/>
);
}
// Override behavior per call
function AdvancedSearch() {
const [query, setQuery] = useState('');
const { debounce } = useDebounce({
delayInMilliSecs: 500,
onTrigger: async () => {
console.log('Default search');
},
});
const handleQuickSearch = () => {
// Override delay for this specific call
debounce({
delayInMilliSecs: 100,
onTrigger: async () => {
console.log('Quick search!');
},
});
};
return (
<>
debounce()} />
>
);
}
`Props:
`tsx
interface UseDebounceProps {
delayInMilliSecs: number;
onTrigger: () => void | Promise;
onPreTrigger?: () => void | Promise;
onPostTrigger?: () => void | Promise;
onDebounceInvocation?: () => void | Promise;
}// Debounce function accepts optional overrides
type DebounceFunction = (
prop?: Partial>
) => void;
`Returns:
`tsx
{
debounce: DebounceFunction;
cancelDebounce: () => void;
}
`Note: The hook stores props in a ref, so they won't update when the component re-renders. To use updated values from component state, either pass them as parameters to
debounce() or access them via closure in the callback functions.$3
Interval timer hook.
`tsx
import { useInterval } from '@shaquillehinds/react-native-essentials';function Component() {
useInterval({
callback: () => console.log('Tick'),
delay: 1000,
start: true,
});
}
`$3
Translate text using LocalizationProvider.
`tsx
import { useTranslation } from '@shaquillehinds/react-native-essentials';function Component() {
const translated = useTranslation({ text: 'Hello World' });
return {translated} ;
}
`$3
Infinite scroll/pagination hook.
`tsx
import { useScrollableItems } from '@shaquillehinds/react-native-essentials';function InfiniteList() {
const {
items,
loading,
onLayout,
onItemsEndReached,
onRefreshItems,
refreshingItems,
updateListItem,
removeListItem,
} = useScrollableItems({
limit: 20,
minFetchDuration: 300,
fetchCooldown: 500,
itemsFetchingFunction: async ({ limit, skip }) => {
const response = await fetch(
/api/items?limit=${limit}&skip=${skip});
return response.json();
},
}); return (
data={items}
onLayout={onLayout}
onEndReached={onItemsEndReached}
onRefresh={onRefreshItems}
refreshing={refreshingItems}
ListFooterComponent={loading ? : null}
/>
);
}
`Props:
`tsx
interface UseScrollableItemsProps {
limit?: number;
minFetchDuration?: number;
fetchCooldown?: number;
itemsFetchingFunction: ItemsFetchingFunction;
itemsFetchingFunctionArgs?: FetchingFunctionArgs;
}type ItemsFetchingFunction = (props: {
limit: number;
skip: number;
}) => Promise;
`Returns:
`tsx
{
items: ListItem[];
setItems: (items: ListItem[]) => void;
loading: boolean;
setLoading: (loading: boolean) => void;
allItemsFetched: boolean;
refreshingItems: boolean;
onLayout: () => void;
onItemsEndReached: () => void;
onRefreshItems: () => Promise;
fetchItems: (props?: { refresh: boolean }) => Promise;
updateListItem: (id: string, key: keyof ListItem, updatedItem: ListItem) => void;
removeListItem: (id: string, key: keyof ListItem) => void;
}
`$3
Access font size scale.
`tsx
import { useFontSizes } from '@shaquillehinds/react-native-essentials';function Component() {
const fontSizes = useFontSizes();
return Text ;
}
`$3
Measure view dimensions.
`tsx
import { useViewDimensions } from '@shaquillehinds/react-native-essentials';function Component() {
const { onLayout, width, height } = useViewDimensions();
return (
Width: {width}, Height: {height}
);
}
`$3
Manage TextInput refs.
`tsx
import { useInputRef } from '@shaquillehinds/react-native-essentials';function Form() {
const { ref, focus, blur } = useInputRef();
return (
<>
>
);
}
`$3
Listen to keyboard events.
`tsx
import { useKeyboardListeners } from '@shaquillehinds/react-native-essentials';function Component() {
useKeyboardListeners({
onShow: (e) => console.log('Keyboard shown', e.endCoordinates.height),
onHide: () => console.log('Keyboard hidden'),
});
}
`---
Utilities
$3
Create typed MMKV storage accessors.
`tsx
import { createStorageAccessors } from '@shaquillehinds/react-native-essentials';interface UserData {
name: string;
age: number;
}
const userStorage = createStorageAccessors('user-key');
// Store data
userStorage.store({ name: 'John', age: 30 });
// Retrieve data
const user = userStorage.retrieve(); // UserData | undefined
// Remove data
const removedUser = userStorage.remove();
// Use hooks
function Component() {
const [user, setUser] = userStorage.useObject();
return {user?.name} ;
}
`$3
`tsx
import {
relativeX,
relativeY,
relativeShort,
relativeLong,
normalize,
} from '@shaquillehinds/react-native-essentials';const width = relativeX(50); // 50% of screen width
const height = relativeY(25); // 25% of screen height
const size = relativeShort(10); // 10% of shorter dimension
const fontSize = normalize(16); // Normalized font size
`$3
`tsx
import {
filterMap,
advancedMap,
keepArrayUnique,
} from '@shaquillehinds/react-native-essentials';// Filter and map in one operation
const result = filterMap(array, (item) => {
if (item.active) return item.name;
return null; // Filtered out
});
// Keep array unique
const unique = keepArrayUnique({
prevItems: existingItems,
newItems: newItems,
comparatorExtractor: (item) => item.id,
});
`$3
`tsx
import { measureAsync } from '@shaquillehinds/react-native-essentials';const measurements = await measureAsync(viewRef);
// Returns: { x, y, width, height, pageX, pageY }
`$3
`tsx
import { snapShotGestureResponderEvent } from '@shaquillehinds/react-native-essentials';const snapshot = snapShotGestureResponderEvent(event);
`$3
`tsx
import { wait } from '@shaquillehinds/react-native-essentials';await wait(1000); // Wait 1 second
`$3
`tsx
import { Scheduler } from '@shaquillehinds/react-native-essentials';// Timer (runs once after delay)
const timer = new Scheduler.Timer(() => {
console.log('Timer fired');
}, 1000);
timer.start();
timer.stop();
// Schedule (runs repeatedly)
const schedule = new Scheduler.Schedule(() => {
console.log('Tick');
}, 1000);
schedule.start();
schedule.stop();
`$3
`tsx
import {
mergeStringRecords,
mergeLanguagesRecords,
type LanguageCode,
type LanguagesRecord,
} from '@shaquillehinds/react-native-essentials';const merged = mergeLanguagesRecords({
obj1: languageRecords1,
obj2: languageRecords2,
});
`$3
`tsx
import { getSequantialRandomId } from '@shaquillehinds/react-native-essentials';const id = getSequantialRandomId(); // Returns unique ID
`---
Animations
$3
Generic animation component wrapper using React Native's Animated API.
`tsx
import { AnimateComponent } from '@shaquillehinds/react-native-essentials';
import { Animated } from 'react-native';// Number-based animation
initialPosition={0}
toPosition={{
type: 'timing',
toValue: 1,
duration: 500,
useNativeDriver: true,
}}
style={(value) => ({
opacity: value,
})}
autoStart
>
{children}
// XY position animation
initialPosition={{ x: 0, y: 0 }}
toPosition={[
{
type: 'timing',
toValue: { x: 100, y: 100 },
duration: 500,
useNativeDriver: true,
}
]}
style={(value) => ({
transform: [{ translateX: value.x }, { translateY: value.y }],
})}
autoStart
loop={3}
>
{children}
`Props:
`tsx
type AnimateComponentProps = {
initialPosition: T; // number | XYNumber ({ x: number, y: number })
toPosition:
| AnimateComponentAnimationConfig
| AnimateComponentAnimationConfig[];
style: (
value: T extends XY ? Animated.AnimatedValueXY : Animated.Value
) => ViewStyle;
autoStart?: boolean;
returnToStart?: boolean;
loop?: number;
ref?: React.RefObject>;
children?: ReactNode;
};type AnimateComponentAnimationConfig =
| (Animated.TimingAnimationConfig & { type: 'timing' })
| (Animated.SpringAnimationConfig & { type: 'spring' })
| (Animated.DecayAnimationConfig & { type: 'decay' });
type AnimateComponentRef = {
stop: () => void;
start: () => void;
reset: () => void;
setValue: (value: T) => void;
value: T extends XY ? Animated.AnimatedValueXY : Animated.Value;
};
`$3
Animate using a shared Animated value.
`tsx
import { AnimateValueComponent } from '@shaquillehinds/react-native-essentials';
import { Animated } from 'react-native';
import { useRef } from 'react';function Component() {
const opacity = useRef(new Animated.Value(0)).current;
// Trigger animation
Animated.timing(opacity, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
return (
initialPosition={0}
toPosition={{
type: 'timing',
toValue: 1,
duration: 500,
useNativeDriver: true,
}}
style={(value) => ({
opacity: value,
})}
>
{children}
);
}
`$3
Animate X and Y coordinates using Animated.ValueXY.
`tsx
import { AnimateXYValueComponent } from '@shaquillehinds/react-native-essentials'; initialPosition={{ x: 0, y: 0 }}
toPosition={{
type: 'spring',
toValue: { x: 100, y: 100 },
useNativeDriver: true,
}}
style={(value) => ({
transform: [{ translateX: value.x }, { translateY: value.y }],
})}
autoStart
>
{children}
;
`$3
Spinning arc loader animation.
`tsx
import { ArcSpinnerAnimation } from '@shaquillehinds/react-native-essentials'; ;
`$3
Hook for drag animations.
`tsx
import { useDragAnimation } from '@shaquillehinds/react-native-essentials';function DraggableBox() {
const { translateX, translateY, gesture } = useDragAnimation();
return (
style={[
{ width: 100, height: 100 },
{ transform: [{ translateX }, { translateY }] },
]}
/>
);
}
`---
Gestures
$3
Drag gesture component.
`tsx
import { Drag } from '@shaquillehinds/react-native-essentials'; onDragStart={() => console.log('Drag started')}
onDragEnd={() => console.log('Drag ended')}
onDrag={(x, y) => console.log('Dragging', x, y)}
>
{children}
;
`$3
Swipe gesture detection.
`tsx
import { Swipe } from '@shaquillehinds/react-native-essentials'; onSwipeLeft={() => console.log('Swiped left')}
onSwipeRight={() => console.log('Swiped right')}
onSwipeUp={() => console.log('Swiped up')}
onSwipeDown={() => console.log('Swiped down')}
>
{children}
;
`$3
Two-finger long press gesture.
`tsx
import { TwoFingerLongPress } from '@shaquillehinds/react-native-essentials'; onLongPress={() => console.log('Two finger long press')}
duration={500}
>
{children}
;
`---
Styles
$3
Platform-agnostic shadow styling.
`tsx
import { shadow } from '@shaquillehinds/react-native-essentials';{children} ;
`$3
Spacing utility type and transformer.
`tsx
import {
type Spacing,
transformSpacing,
} from '@shaquillehinds/react-native-essentials';const spacing: Spacing = {
all: 10,
horizontal: 15,
vertical: 20,
top: 5,
};
const style = transformSpacing({
margin: spacing,
orientation: deviceOrientation,
});
`---
Algorithms
String, number, and data manipulation utilities.
$3
`tsx
import {
firstLetterCap,
titleCase,
camelCase,
snakeToCamelCase,
dashToCamelCase,
toCamelCase,
snakeToTitleCase,
dashToTitleCase,
toTitleCase,
camelToUpperSnake,
} from '@shaquillehinds/react-native-essentials';firstLetterCap('hello'); // "Hello"
titleCase('hello world'); // "Hello World"
camelCase('hello world'); // "helloWorld"
snakeToCamelCase('hello_world'); // "helloWorld"
toTitleCase('hello-world'); // "Hello World"
camelToUpperSnake('helloWorld'); // "HELLO_WORLD"
`$3
`tsx
import {
kFormatter,
centsToDollars,
hexOpacity,
secondsToTime,
getTodayInSeconds,
} from '@shaquillehinds/react-native-essentials';kFormatter(1500); // "1.5k"
kFormatter(500); // 500
centsToDollars({
cents: 1999,
dollarSign: true,
currency: 'USD',
}); // "$19.99 USD"
hexOpacity(50); // "80" (50% opacity in hex)
hexOpacity(0.5); // "80" (also accepts decimals)
secondsToTime(3665); // "01:01:05"
secondsToTime(3665, { hideHours: true }); // "61:05"
getTodayInSeconds(); // Current time in seconds since midnight
`$3
`tsx
import { toBase64, fromBase64 } from '@shaquillehinds/react-native-essentials';const encoded = toBase64('Hello World');
const decoded = fromBase64(encoded);
`$3
`tsx
import { _24hrsToDate } from '@shaquillehinds/react-native-essentials';const date = _24hrsToDate('1430'); // Date object for 14:30 (2:30 PM)
`$3
`tsx
import {
objToQueryStr,
deepCopy,
} from '@shaquillehinds/react-native-essentials';objToQueryStr({ page: 1, limit: 20 }); // "page=1&limit=20"
const copy = deepCopy(originalObject); // Deep clone
`---
Type Definitions
$3
`tsx
// Font Types
type FontSize =
| 'headingL'
| 'headingM'
| 'headingS'
| 'titleL'
| 'titleM'
| 'titleS'
| 'bodyL'
| 'bodyM'
| 'bodyS';type FontStyle =
| 'Thin'
| 'Extra Light'
| 'Light'
| 'Regular'
| 'Medium'
| 'SemiBold'
| 'Bold'
| 'ExtraBold'
| 'Black';
type LineHeight = 'short' | 'tall';
type LetterSpacing = 'wide' | 'extraWide';
// Button Types
type ButtonSize = 'small' | 'medium' | 'large' | 'auto' | 'wide';
type BorderSize = 'razor' | 'thin' | 'medium' | 'large';
type RadiusSize = 'edgy' | 'sharp' | 'medium' | 'soft' | 'curvy' | 'round';
// Device Types
type DeviceOrientation = 'portrait' | 'landscape';
// Spacing Types
type Spaces = [number, number?, number?, number?];
interface Spacing {
/**
* CSS-style padding/margin shorthand using percentages:
* - [10] = all sides 10%
* - [10, 20] = vertical 10%, horizontal 20%
* - [10, 20, 30] = top 10%, horizontal 20%, bottom 30%
* - [10, 20, 30, 40] = top 10%, right 20%, bottom 30%, left 40%
*/
padding?: Spaces;
margin?: Spaces;
}
`---
Best Practices
$3
1. Use
useDeviceOrientation for responsive layouts instead of recalculating dimensions
2. Leverage useScrollableItems for efficient pagination
3. Use useDebounce for expensive operations
4. Memoize callbacks and values when using hooks$3
1. Always provide type parameters for generic utilities:
`tsx
createStorageAccessors('user')
useScrollableItems- (...)
`2. Use provided types for consistent styling:
`tsx
const spacing: Spacing = { all: 10 };
`$3
1. Use
Layout component for consistent spacing:
`tsx
`2. Prefer percentage-based dimensions with
relativeX/relativeY
3. Use normalize() for font sizes to ensure consistency across devicesSpacing Examples:
`tsx
// All sides
padding={[5]} // 5% on all sides// Vertical and horizontal
padding={[5, 10]} // 5% top/bottom, 10% left/right
// Top, horizontal, bottom
padding={[5, 10, 3]} // 5% top, 10% left/right, 3% bottom
// Individual sides
padding={[5, 10, 3, 8]} // 5% top, 10% right, 3% bottom, 8% left
`$3
1. Wrap app in
LocalizationProvider for translation support
2. Use Translate component or useTranslation hook
3. Provide initialLanguagesRecord for offline support---
Migration Guide
$3
- Updated peer dependencies to latest versions
- Added new animation components
- Improved TypeScript definitions
- Enhanced gesture system
---
Troubleshooting
$3
Issue: Components not rendering
- Ensure all peer dependencies are installed
- Check that providers wrap your app correctly
Issue: Animations not working
- Verify
react-native-reanimated is properly configured
- Check Reanimated plugin in babel.config.jsIssue: Storage not persisting
- Ensure
react-native-mmkv` is properly linked---
For issues, questions, or contributions:
- GitHub Issues
- GitHub Repository
MIT © Shaquille Hinds