Building Custom Components
@masumdev/rn-ui exports reusable base TypeScript interfaces and theme hooks (useTheme, useThemeStyles) so that you can easily build your own custom React Native components while maintaining full visual and type-safe consistency across your design system.
Design System Architectural Rules
To ensure long-term consistency across the monorepo, all @masumdev/rn-ui components follow two strict architectural rules:
1. Orthogonal Color (tone) vs Structure (variant)
tonecontrols semantic color intent ('primary','secondary','accent','success','warning','danger','info','default').variantcontrols visual fill & structure ('filled','outline','ghost','soft','solid').- Never combine color names into variant strings (e.g. use
tone="danger" variant="outline"instead ofvariant="danger-outline").
2. Selective Base Interface Extension
- Interactive Action Controls (
Button,IconButton,Badge,Alert,Switch,Slider,Rating,FloatingActionButton) MUST extend exported base interfaces (ToneProps,VariantProps,SizeProps,ShapeProps,IconSlotsProps,BaseUIComponentProps). - Layout & Structure Primitives (
Box,Divider,AspectRatio,Calendar,Table) stay clean and lean without extending irrelevant control props.
3. Strict Type Safety (Zero any Allowed)
- Zero
anyTypes: Usinganytype casting oranyparameter types is strictly forbidden. Always use strict React Native event types (e.g.NativeSyntheticEvent<ImageLoadEventData>,LayoutChangeEvent) or precise generics (React.isValidElement<{ style?: StyleProp<ViewStyle> }>(child)).
Extending Base UI Interfaces
| Prop | Type | Default | Description |
|---|---|---|---|
BaseUIComponentProps | interface BaseUIComponentProps<V, T, S> | — | Composite interface combining variant, tone, size, disabled, and loading props. |
ToneProps | interface ToneProps<T> | — | Accepts tone prop for semantic colors (primary, success, danger, etc.). |
VariantProps | interface VariantProps<V> | — | Accepts variant prop for visual fill/structure (filled, outline, soft, ghost). |
SizeProps | interface SizeProps<S> | — | Accepts size prop for dimension scaling (xs, sm, md, lg, xl). |
ShapeProps | interface ShapeProps<S> | — | Accepts shape prop for border radius styling (rounded, pill, square). |
IconSlotsProps | interface IconSlotsProps | — | Accepts leftIcon and rightIcon props of type RenderIcon. |
Example: Building a Custom Card Component
Here is a complete, production-ready example of creating a <CustomCard /> component by extending BaseUIComponentProps:
import React from 'react';import { Pressable, View } from 'react-native';import { BaseUIComponentProps, Text, useTheme, renderIcon, RenderIcon,} from '@masumdev/rn-ui';
export interface CustomCardProps extends BaseUIComponentProps<'filled' | 'outline', 'primary' | 'accent' | 'danger'> { title: string; subtitle?: string; icon?: RenderIcon; onPress?: () => void;}
export function CustomCard({ variant = 'filled', tone = 'primary', size = 'md', title, subtitle, icon, onPress, disabled, loading,}: CustomCardProps) { const { colors, radii, spacing, components } = useTheme();
// Resolve color palette from tone const toneColor = tone === 'danger' ? colors.danger : tone === 'accent' ? colors.accent : colors.primary;
const isOutline = variant === 'outline';
return ( <Pressable disabled={disabled || loading} onPress={onPress} style={({ pressed }) => ({ padding: size === 'sm' ? spacing.sm : spacing.md, borderRadius: radii.xl, borderWidth: isOutline ? components.borderWidth.strong : 0, borderColor: isOutline ? toneColor : 'transparent', backgroundColor: isOutline ? colors.surface : toneColor, opacity: disabled ? 0.5 : pressed ? 0.85 : 1, flexDirection: 'row', alignItems: 'center', gap: spacing.md, })} > {icon ? renderIcon(icon, isOutline ? toneColor : colors.surface, 24) : null} <View style={{ flex: 1 }}> <Text variant="label" style={{ color: isOutline ? toneColor : colors.surface }} > {title} </Text> {subtitle ? ( <Text variant="bodySmall" style={{ color: isOutline ? colors.textMuted : colors.surface }} > {subtitle} </Text> ) : null} </View> </Pressable> );}Key Best Practices
- Use
useTheme()for Dynamic Tokens: Always dereferencecolors,spacing,radii, andcomponentsinside your component so it automatically adapts to light and dark theme mode toggles. - Use
renderIcon()Helper: Pass icon props throughrenderIcon(icon, color, size)to seamlessly support both JSX icon elements (e.g.<Search />) and function renderers (e.g.({ color, size }) => <Search color={color} size={size} />). - Extend
BaseUIComponentProps: Generics allow you to restrict acceptablevariantortonevalues specifically for your component (e.g.BaseUIComponentProps<'filled' | 'outline', 'primary' | 'accent'>). - Use Exported Theme Types for 100% Type-Safety: Always import
ThemeInput,ThemeColors,Theme, orThemeStyleFactoryfrom@masumdev/rn-uiwhen creating custom themes or component style generators to get full IDE autocompletion and prevent type errors.