@shayrn/react-native-scaler
Version:
Responsive scaling utilities for React Native CLI and Expo apps
561 lines (424 loc) • 15.1 kB
Markdown
# react-native-scaler
A production-ready scaling utility for React Native applications that enables consistent, responsive UI across different device sizes and screen densities. Built with TypeScript, fully tested, and designed to bridge the gap between your design specifications (Figma, Sketch, etc.) and real-world device screens.
## Why This Library?
React Native's default styling doesn't account for varying screen dimensions across devices. A UI that looks perfect on an iPhone 14 may appear cramped on an iPhone SE or oversized on an iPad. This library solves that problem by providing intelligent scaling functions that:
- **Maintain design proportions** across all devices
- **Respect user accessibility preferences** (font scaling, display zoom)
- **Ensure pixel-perfect rendering** on high-DPI displays
- **Handle orientation changes** automatically
- **Provide device-aware adaptive scaling** (tablets vs phones)
## Installation
```bash
npm install @shayrn/react-native-scaler
# or
yarn add @shayrn/react-native-scaler
# or
pnpm add @shayrn/react-native-scaler
# or
bun add @shayrn/react-native-scaler
```
For Expo projects:
```bash
npx expo install @shayrn/react-native-scaler
```
## Quick Start
```typescript
import { scale, scaleFont } from '@shayrn/react-native-scaler';
import { View, Text, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
padding: scale(16),
margin: scale(20),
borderRadius: scale(12),
},
title: {
fontSize: scaleFont(24),
marginBottom: scale(12),
},
});
```
## Configuration
By default, the library uses a reference dimension of 402×874 (based on common design specs). If your design uses different dimensions, configure them once at app startup:
```typescript
import { configureScaler } from '@shayrn/react-native-scaler';
// In your App.tsx or _layout.tsx
configureScaler({
referenceWidth: 390, // Your design's base width
referenceHeight: 844, // Your design's base height
});
```
**When to configure:**
- Your design mockups use specific device dimensions (e.g., iPhone 14: 390×844)
- You're working with existing designs that don't match the default
- You want to match a specific design system
## Core API
### Basic Scaling
#### `scale(size, options?)`
Universal scaling function that maintains proportions across devices.
```typescript
// Basic usage
const padding = scale(16);
// With constraints
const padding = scale(16, { min: 12, max: 24 });
// Width-based scaling (useful for horizontal layouts)
const width = scale(100, { useUniform: false });
```
**Options:**
- `min`: Minimum pixel value (prevents too-small sizing)
- `max`: Maximum pixel value (prevents too-large sizing)
- `useUniform`: Use uniform scale (default) or width-only scale
#### `scaleWidth(size, options?)` / `scaleHeight(size, options?)`
Scale based on a single dimension.
```typescript
const cardWidth = scaleWidth(300);
const cardHeight = scaleHeight(200, { min: 150, max: 250 });
```
**Use cases:**
- `scaleWidth`: Horizontal elements, cards, containers
- `scaleHeight`: Vertical elements, lists, scroll views
#### `scaleRadius(radius, options?)`
Specialized function for border radius scaling.
```typescript
const borderRadius = scaleRadius(12, { min: 8, max: 16 });
```
### Typography Scaling
#### `scaleFont(fontSize, options?)`
Font size scaling with accessibility support.
```typescript
// Respects user's accessibility font scale (default)
const fontSize = scaleFont(16);
// With constraints
const fontSize = scaleFont(16, { min: 14, max: 20 });
// Ignore accessibility settings (not recommended)
const fontSize = scaleFont(16, { respectAccessibility: false });
```
**Default constraints:** min: 10, max: 100
**Accessibility:** By default, font sizes respect the user's system font scale preference. This ensures your app remains accessible to users with visual impairments.
#### `scaleLineHeight(lineHeight, options?)`
Line height scaling with the same accessibility considerations as fonts.
```typescript
const lineHeight = scaleLineHeight(24, { min: 20, max: 32 });
```
### Responsive Helper
For more semantic code, use the `responsive` object:
```typescript
import { responsive } from '@shayrn/react-native-scaler';
const styles = {
container: {
padding: responsive.spacing(16),
margin: responsive.adaptiveSpacing(20), // Auto-adjusts for tablets
borderRadius: responsive.radius(12, { min: 8, max: 16 }),
},
text: {
fontSize: responsive.font(16, { min: 14, max: 20 }),
lineHeight: responsive.lineHeight(24),
},
separator: {
height: responsive.pixelPerfect(1), // Crisp 1px line
},
};
```
**Available methods:**
- `responsive.width(value, options?)`
- `responsive.height(value, options?)`
- `responsive.font(value, options?)`
- `responsive.lineHeight(value, options?)`
- `responsive.radius(value, options?)`
- `responsive.spacing(value, options?)`
- `responsive.adaptiveSpacing(value)` - Auto-adjusts for device type
- `responsive.pixelPerfect(value)` - Pixel-perfect rendering
## Advanced Features
### Device Information
Access detailed device information for conditional rendering:
```typescript
import { deviceInfo } from '@shayrn/react-native-scaler';
if (deviceInfo.isTablet) {
// Render tablet-specific UI
}
console.log({
pixelRatio: deviceInfo.pixelRatio, // 2.0, 3.0, etc.
fontScale: deviceInfo.fontScale, // User's font scale
isHighDensity: deviceInfo.isHighDensity, // >= 2x pixel ratio
isTablet: deviceInfo.isTablet, // Min dimension >= 768
isSmallDevice: deviceInfo.isSmallDevice, // Min dimension < 350
aspectRatio: deviceInfo.aspectRatio, // Width/height ratio
});
```
### Adaptive Scaling
Automatically adjust spacing based on device type:
```typescript
import { responsive, deviceInfo } from '@shayrn/react-native-scaler';
const styles = {
// Auto-adjusts: 1.2x for tablets, 0.8x for small devices
container: {
padding: responsive.adaptiveSpacing(16),
},
// Manual device-specific styling
title: {
fontSize: deviceInfo.isTablet
? responsive.font(24, { min: 20, max: 28 })
: responsive.font(18, { min: 16, max: 20 }),
},
};
```
### Orientation Change Handling
React to device orientation changes:
```typescript
import { onOrientationChange } from '@shayrn/react-native-scaler';
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
const subscription = onOrientationChange(() => {
// Scales are automatically recalculated
console.log('Orientation changed');
// Force re-render if needed
forceUpdate();
});
return () => subscription.remove();
}, []);
// Component code...
}
```
### Pixel-Perfect Rendering
Ensure crisp rendering on all devices:
```typescript
import { getPixelSize, responsive } from '@shayrn/react-native-scaler';
const styles = {
// Method 1: Using responsive helper
separator: {
height: responsive.pixelPerfect(1),
backgroundColor: '#E0E0E0',
},
// Method 2: Using getPixelSize directly
border: {
borderWidth: getPixelSize(1),
},
};
```
**Use cases:**
- Hairline borders and separators
- Grid lines
- Small icons and indicators
- Any element that must be visually consistent across devices
### Pre-built Responsive Styles
Quick-start style objects for common patterns:
```typescript
import { responsiveStyles } from '@shayrn/react-native-scaler';
// Use as-is
<View style={responsiveStyles.container}>
<Text style={responsiveStyles.text}>Hello</Text>
</View>
// Or extend
<View style={[responsiveStyles.container, { backgroundColor: 'white' }]}>
<Text style={[responsiveStyles.adaptiveText, { color: 'blue' }]}>
Adaptive Text
</Text>
</View>
```
**Available styles:**
- `responsiveStyles.container` - Standard padding, margin, radius, gap
- `responsiveStyles.text` - Standard font size and line height
- `responsiveStyles.adaptiveText` - Device-aware text (larger on tablets)
## Debugging
Access current scale factors for debugging:
```typescript
import { getScales, screenWidth, screenHeight } from '@shayrn/react-native-scaler';
console.log('Screen:', screenWidth, 'x', screenHeight);
const scales = getScales(); // Use getter for current values
console.log({
width: scales.width, // Width scale factor
height: scales.height, // Height scale factor
uniform: scales.uniform, // Uniform scale (min of width/height)
pixel: scales.pixel, // Device pixel ratio
font: scales.font, // User's font scale
});
```
**Note:** Use `getScales()` instead of the deprecated `scales` export to get accurate values after orientation changes.
## Complete Example
```typescript
import React, { useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import {
responsive,
deviceInfo,
onOrientationChange,
configureScaler
} from '@shayrn/react-native-scaler';
// Configure once at app startup
configureScaler({
referenceWidth: 390,
referenceHeight: 844,
});
export function ProductCard() {
useEffect(() => {
const subscription = onOrientationChange(() => {
// Handle orientation change
});
return () => subscription.remove();
}, []);
return (
<View style={styles.card}>
<Text style={styles.title}>Product Title</Text>
<Text style={styles.description}>
This card scales beautifully across all devices while maintaining
the designer's intended proportions.
</Text>
</View>
);
}
const styles = StyleSheet.create({
card: {
padding: responsive.adaptiveSpacing(16),
margin: responsive.spacing(12, { min: 8, max: 20 }),
borderRadius: responsive.radius(12, { min: 8, max: 16 }),
borderWidth: responsive.pixelPerfect(1),
borderColor: '#E0E0E0',
backgroundColor: 'white',
},
title: {
fontSize: responsive.font(18, {
min: 16,
max: deviceInfo.isTablet ? 24 : 20
}),
lineHeight: responsive.lineHeight(26, { min: 20, max: 32 }),
fontWeight: '600',
marginBottom: responsive.spacing(8),
},
description: {
fontSize: responsive.font(14, { min: 12, max: 16 }),
lineHeight: responsive.lineHeight(20, { min: 16, max: 24 }),
color: '#666',
},
});
```
## How It Works
### Scaling Algorithm
The library calculates scale factors based on the ratio between device dimensions and reference dimensions:
```
widthScale = deviceWidth / referenceWidth
heightScale = deviceHeight / referenceHeight
uniformScale = Math.min(widthScale, heightScale)
```
**Uniform scale** (default) uses the smaller of the two scales to maintain aspect ratio and prevent distortion.
### Pixel Rounding
All scaled values are rounded using React Native's `PixelRatio.roundToNearestPixel()` to ensure:
- Crisp rendering on all devices
- No sub-pixel rendering artifacts
- Proper alignment with device pixels
### Accessibility Integration
Font scaling integrates with React Native's accessibility APIs:
- Reads user's system font scale via `PixelRatio.getFontScale()`
- Applies scale factor by default (can be disabled)
- Maintains readability for users with visual impairments
## TypeScript Support
Full TypeScript support with exported types:
```typescript
import type {
ScaleOptions,
FontScaleOptions,
DeviceInfo
} from '@shayrn/react-native-scaler';
const options: ScaleOptions = {
min: 10,
max: 100,
useUniform: true,
};
const fontOptions: FontScaleOptions = {
min: 12,
max: 20,
respectAccessibility: true,
};
```
## Platform Support
- **iOS**: Full support (iOS 13+)
- **Android**: Full support (Android 5.0+)
- **Tablets**: Full support with adaptive scaling
- **Expo**: Full support (SDK 48+)
- **React Native**: 0.64+
## Performance
The library is designed for production use with minimal overhead:
- **No runtime dependencies** (peer depends on react-native only)
- **Simple mathematical calculations** (no heavy computations)
- **Scales calculated once** (updated only on configuration or orientation change)
- **Bundle size**: < 5KB minified
## Best Practices
### Do's
✅ Configure reference dimensions to match your designs
✅ Use constraints (min/max) to prevent extreme scaling
✅ Respect accessibility settings (default behavior)
✅ Use adaptive spacing for better tablet UX
✅ Use pixel-perfect rendering for thin lines/borders
### Don'ts
❌ Don't scale everything - some values (like 1px borders) should stay fixed
❌ Don't set extreme min/max constraints that break on edge cases
❌ Don't disable accessibility unless absolutely necessary
❌ Don't use width-based scaling for vertical elements (and vice versa)
❌ Don't reconfigure scaler dimensions mid-session
## Migration Guide
### From v1.x to v2.x
No breaking changes - all v1.x code continues to work. New features:
- Enhanced constraint system (min/max options)
- Accessibility support for fonts (enabled by default)
- Device-aware adaptive scaling
- Pixel-perfect rendering utilities
- Orientation change handling
```typescript
// v1.x - still works
const size = responsive.font(14);
// v2.x - enhanced
const size = responsive.font(14, {
min: 12,
max: 18,
respectAccessibility: true
});
```
### Deprecations
- `scales` export is deprecated in favor of `getScales()` function for accurate post-orientation values
## Contributing
Contributions are welcome! This project uses **automated versioning and publishing**.
### Quick Start
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes with tests
4. Commit using [Conventional Commits](https://www.conventionalcommits.org/):
- `feat: add new feature` → Minor version bump (1.0.0 → 1.1.0)
- `fix: resolve bug` → Patch version bump (1.0.0 → 1.0.1)
- `feat!: breaking change` → Major version bump (1.0.0 → 2.0.0)
5. Push and create a Pull Request
6. Once merged, the version is **automatically bumped and published to npm**! 🚀
**Full guidelines:** See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed instructions.
### Development Setup
```bash
# Clone the repo
git clone https://github.com/maheshmuttintidev/react-native-scaler.git
cd react-native-scaler
# Install dependencies
npm install
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Build
npm run build
# Lint
npm run lint
# Format
npm run format
```
### Automated Release Process
This project automatically:
- ✅ Analyzes commit messages to determine version bump
- ✅ Runs all tests before publishing
- ✅ Bumps version in package.json
- ✅ Publishes to npm
- ✅ Creates GitHub release with changelog
- ✅ No manual version management needed!
See [CONTRIBUTING.md](CONTRIBUTING.md) for commit message guidelines.
## License
MIT © Mahesh Muttinti
## Links
- **GitHub**: https://github.com/maheshmuttintidev/react-native-scaler
- **npm**: https://www.npmjs.com/package/@shayrn/react-native-scaler
- **Issues**: https://github.com/maheshmuttintidev/react-native-scaler/issues
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for release history.