@vincent-huy-uit/react-native-responsive-ui
v1.1.7
Published
A responsive design library for React Native / Expo applications. Automatically scale design values across different screen sizes.
Maintainers
Readme
@vincent-huy-uit/react-native-responsive-ui
A zero-config responsive design library for React Native / Expo.
Build UIs that look perfect on phones, tablets, and iPads with minimal code.
📚 Documentation
🎯 Problems We Solve
| ❌ Problem | ✅ Solution |
|-----------|------------|
| Wrapping every value with scaling functions is tedious | createScaledStyles() - auto-scales entire StyleSheet |
| Manual size calculations for different screens | s(16) - scales any value automatically |
| Duplicating code for breakpoints | responsive() - pick values per device |
| Different layouts needed for phone vs tablet | ResponsiveSwitch - render different components |
| Inconsistent spacing/fonts across app | Design tokens - space.md, font.body |
| iPads scale too large with width-only scaling | Aspect-aware scaling - works on all devices by default |
📦 Installation
npm install @vincent-huy-uit/react-native-responsive-ui🚀 The 3 Main Features
1️⃣ createScaledStyles() - Auto-Scale Entire StyleSheet ⭐
The easiest way! Drop-in replacement for
StyleSheet.create()- scales everything automatically.
import { createScaledStyles } from '@vincent-huy-uit/react-native-responsive-ui';
// Just write normal values - everything scales automatically!
const styles = createScaledStyles({
container: {
padding: 16, // ✅ Auto-scaled
borderRadius: 12, // ✅ Auto-scaled
flex: 1, // ✅ NOT scaled (it's a ratio)
opacity: 0.9, // ✅ NOT scaled (0-1 value)
},
title: {
fontSize: 24, // ✅ Auto-scaled
fontWeight: '600', // ✅ Preserved as-is
},
shadow: {
shadowOffset: { width: 2, height: 4 }, // ✅ Nested values scaled too!
shadowRadius: 8, // ✅ Auto-scaled
shadowOpacity: 0.2, // ✅ NOT scaled
},
});Smart exclusions - these properties are NOT scaled:
flex,flexGrow,flexShrink(ratios)opacity,shadowOpacity(0-1 values)zIndex,elevation(indices)aspectRatio,scale,fontWeight
1️⃣ s() - Scale Individual Values
Use when you need to scale a single value (e.g., inline styles, dynamic values)
import { s } from '@vincent-huy-uit/react-native-responsive-ui';
// For inline styles or dynamic values
<View style={{ padding: s(16), marginTop: s(isLarge ? 32 : 16) }} />🎨 Design Tokens (Alternative to s())
Pre-defined scaled values for consistency:
import { space, font, radius } from '@vincent-huy-uit/react-native-responsive-ui';
const styles = StyleSheet.create({
card: {
padding: space.md, // 16pt scaled
fontSize: font.body, // 16pt scaled
borderRadius: radius.md, // 8pt scaled
},
});| space | font | radius |
|---------|--------|----------|
| xs = 4 | caption = 12 | sm = 4 |
| sm = 8 | body = 16 | md = 8 |
| md = 16 | subtitle = 18 | lg = 16 |
| lg = 24 | title = 24 | full = 9999 |
| xl = 32 | headline = 32 | |
| xxl = 48 | | |
Custom tokens? Override via Provider:
<ResponsiveProvider
tokens={{
space: { sm: 4, md: 12, lg: 20 },
font: { body: 14, title: 22 },
}}
>
<App />
</ResponsiveProvider>2️⃣ responsive() - Different Values Per Device
Use when you need different values for mobile vs tablet (e.g., grid columns, show/hide elements)
import { responsive, useDeviceType } from '@vincent-huy-uit/react-native-responsive-ui';
function ProductGrid() {
// Pick different values per device
const columns = responsive({ mobile: 2, tablet: 3, desktop: 4 });
const showSidebar = responsive({ mobile: false, tablet: true });
// Or get device type directly
const device = useDeviceType(); // "mobile" | "tablet" | "desktop"
return (
<View style={{ flexDirection: device === 'mobile' ? 'column' : 'row' }}>
{showSidebar && <Sidebar />}
<Grid columns={columns} />
</View>
);
}3️⃣ ResponsiveSwitch - Different Layouts Per Device
Use when mobile and tablet need completely different UIs (e.g., master-detail on tablet)
import { ResponsiveSwitch } from '@vincent-huy-uit/react-native-responsive-ui';
function InboxScreen() {
return (
<ResponsiveSwitch
mobile={<MobileInbox />} // Simple list
tablet={<TabletInbox />} // Master-detail split
/>
);
}
// Mobile: Navigate to detail screen
const MobileInbox = () => (
<EmailList onPress={(id) => navigation.navigate('EmailDetail', { id })} />
);
// Tablet: Side-by-side layout
const TabletInbox = () => (
<View style={{ flexDirection: 'row' }}>
<EmailList style={{ width: '35%' }} onPress={setSelected} />
<EmailDetail style={{ width: '65%' }} email={selected} />
</View>
);📋 Quick Reference
Most Used (Learn These First!)
| Function | What It Does | Example |
|----------|--------------|---------|
| createScaledStyles() ⭐ | Auto-scale entire StyleSheet | Replace StyleSheet.create() |
| s(value) | Scale a single value | padding: s(16) |
| space.md | Scaled spacing token | gap: space.lg |
| font.body | Scaled font token | fontSize: font.title |
| responsive() | Different values per device | responsive({ mobile: 2, tablet: 4 }) |
| useDeviceType() | Get current device type | "mobile" | "tablet" | "desktop" |
| ResponsiveSwitch | Different components per device | <ResponsiveSwitch mobile={...} tablet={...} /> |
All Scaling Functions
| Function | Description |
|----------|-------------|
| s(value) | Universal scaling (default: aspect-aware) |
| sv(value) | Scale based on height |
| sm(value) | Moderate scaling (min of width/height) |
| sa(value) | Aspect-aware scaling |
All Hooks
| Hook | Returns |
|------|---------|
| useDeviceType() | "mobile" | "tablet" | "desktop" |
| useOrientation() | "portrait" | "landscape" |
| useDimensions() | { width, height } |
| useScaleFactor() | Current scale factor |
| useScaledValue(n) | Scaled value (reactive) |
Utilities
| Function | Description |
|----------|-------------|
| getDeviceType() | Get device type (non-reactive) |
| getOrientation() | Get orientation (non-reactive) |
| createScaledStyles() | Auto-scale all numbers in StyleSheet |
💡 Best Practices
✅ Do This
// 1. Use createScaledStyles() for StyleSheets (easiest!)
const styles = createScaledStyles({
container: { padding: 16, fontSize: 14 } // Auto-scaled!
});
// 2. Use s() for inline/dynamic styles
<View style={{ padding: s(16) }} />
// 3. Use tokens for consistency
padding: space.md
fontSize: font.body
// 4. Use responsive() for conditional values
const columns = responsive({ mobile: 2, tablet: 4 });
// 5. Use ResponsiveSwitch only when layouts are completely different
<ResponsiveSwitch mobile={<List />} tablet={<Grid />} />❌ Avoid This
// Don't hardcode sizes in StyleSheet.create()
const styles = StyleSheet.create({
container: { padding: 16 } // ❌ Won't scale!
});
// Don't create separate files for minor differences
// Instead, use responsive() in the same component⚙️ Configuration (Optional)
Zero configuration needed! But you can customize:
Using Provider (Recommended)
import { ResponsiveProvider } from '@vincent-huy-uit/react-native-responsive-ui';
<ResponsiveProvider
baseWidth={375}
baseHeight={812}
scalingStrategy="aspectAware"
maxScaleFactor={2}
breakpoints={{ tablet: 768, desktop: 1024 }}
tokens={{
space: { xs: 2, sm: 4, md: 12, lg: 20, xl: 28, xxl: 40 },
font: { caption: 10, body: 14, subtitle: 16, title: 22, headline: 30 },
radius: { sm: 2, md: 6, lg: 12 },
}}
>
<App />
</ResponsiveProvider>Using configure() (Imperative)
import { configure, configureTokens } from '@vincent-huy-uit/react-native-responsive-ui';
// Configure scaling
configure({
baseWidth: 375,
baseHeight: 812,
scalingStrategy: 'aspectAware',
maxScaleFactor: 2,
breakpoints: { tablet: 768, desktop: 1024 },
});
// Configure tokens (optional)
configureTokens({
space: { md: 12, lg: 20 },
font: { body: 14 },
});Scaling Strategies
| Strategy | Best For |
|----------|----------|
| "aspectAware" | All devices (default) - auto-adjusts for iPads |
| "width" | Phone-only apps |
| "moderate" | iPad-first apps |
🔤 TypeScript
import type {
DeviceType, // "mobile" | "tablet" | "desktop"
Orientation, // "portrait" | "landscape"
ScalingStrategy, // "width" | "height" | "moderate" | "aspectAware"
ResponsiveValues, // { mobile: T; tablet?: T; desktop?: T }
} from '@vincent-huy-uit/react-native-responsive-ui';📱 Real-World Examples
Dashboard with Stats
import { createScaledStyles, responsive } from '@vincent-huy-uit/react-native-responsive-ui';
function Dashboard() {
const columns = responsive({ mobile: 2, tablet: 4 });
return (
<View style={styles.container}>
<Text style={styles.title}>Dashboard</Text>
<View style={styles.statsGrid}>
{stats.map(stat => (
<View key={stat.id} style={[styles.statCard, { width: `${100/columns}%` }]}>
<Text style={styles.statValue}>{stat.value}</Text>
<Text style={styles.statLabel}>{stat.label}</Text>
</View>
))}
</View>
</View>
);
}
// All numeric values auto-scaled!
const styles = createScaledStyles({
container: { padding: 24 },
title: { fontSize: 32, fontWeight: 'bold' },
statsGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 12 },
statCard: { padding: 16, backgroundColor: '#1a1a2e', borderRadius: 12 },
statValue: { fontSize: 24, fontWeight: 'bold' },
statLabel: { fontSize: 12, color: '#888' },
});Product Marketplace
import { createScaledStyles, responsive } from '@vincent-huy-uit/react-native-responsive-ui';
function Marketplace() {
const columns = responsive({ mobile: 2, tablet: 3, desktop: 4 });
return (
<FlatList
data={products}
numColumns={columns}
key={columns}
contentContainerStyle={styles.list}
renderItem={({ item }) => (
<View style={[styles.card, { width: `${100/columns - 2}%` }]}>
<Image source={item.image} style={styles.image} />
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.price}>${item.price}</Text>
</View>
)}
/>
);
}
// Clean code - no s() wrapping needed!
const styles = createScaledStyles({
list: { padding: 16 },
card: { margin: 4, backgroundColor: '#222', borderRadius: 16 },
image: { width: '100%', height: 150, borderRadius: 16 },
name: { fontSize: 16, padding: 8 },
price: { fontSize: 18, color: '#4CAF50', paddingHorizontal: 8 },
});Master-Detail Layout (Inbox)
import { createScaledStyles, ResponsiveSwitch } from '@vincent-huy-uit/react-native-responsive-ui';
function InboxScreen() {
return (
<ResponsiveSwitch
mobile={<MobileInbox />}
tablet={<TabletInbox />}
/>
);
}
// Tablet: Side-by-side master-detail
function TabletInbox() {
const [selected, setSelected] = useState(emails[0]);
return (
<View style={styles.splitView}>
<View style={styles.masterPanel}>
<EmailList emails={emails} onSelect={setSelected} selected={selected} />
</View>
<View style={styles.detailPanel}>
<EmailDetail email={selected} />
</View>
</View>
);
}
const styles = createScaledStyles({
splitView: { flex: 1, flexDirection: 'row' },
masterPanel: { width: '35%', borderRightWidth: 1, borderColor: '#333' },
detailPanel: { width: '65%', padding: 24 },
});📄 License
MIT
