Gathering detailed insights and metrics for @exodus/react-native-number-flow
Gathering detailed insights and metrics for @exodus/react-native-number-flow
Gathering detailed insights and metrics for @exodus/react-native-number-flow
Gathering detailed insights and metrics for @exodus/react-native-number-flow
npm install @exodus/react-native-number-flow
Typescript
Module System
Node Version
NPM Version
Love this project? Help keep it running — sponsor us today! 🚀
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
Latest Version
1.0.2
Package Id
@exodus/react-native-number-flow@1.0.2
Unpacked Size
1.22 MB
Size
892.04 kB
File Count
134
NPM Version
9.5.1
Node Version
18.16.1
Publised On
20 Jan 2025
Cumulative downloads
Total Downloads
Last day
0%
0
Compared to previous day
Last week
0%
0
Compared to previous week
Last month
0%
0
Compared to previous month
Last year
0%
0
Compared to previous year
2
A lightweight and performant number-scrolling animation component for React Native.
Inspired by number-flow (a web-based library).
Effortlessly display dynamically changing numerical values (including decimals, prefixes/suffixes, and percentage signs) with smooth slot-machine-like transitions.
Why Number Flow?
Whether you're building a finance dashboard, a stats panel, or any UI requiring frequent value updates, this component makes your numbers more engaging and visually appealing.
,
by default), including full customization.$
, AED
, etc.), percentage (%
), or any custom prefix/suffix with configurable styles.
Install using your favorite package manager:
1npm install @exodus/react-native-number-flow
Or
1yarn add @exodus/react-native-number-flow
Below is a basic usage example where we animate a numeric value that updates automatically every second. Check out the Props section to learn how to further customize the component.
1import React, { useCallback, useEffect, useState } from 'react'; 2import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; 3import SlotNumberFlow from '@exodus/react-native-number-flow'; 4 5const formatNumber = (num: number) => { 6 return new Intl.NumberFormat('en-US').format(num); 7}; 8 9const App = () => { 10 const [value, setValue] = useState<string>(() => formatNumber(10000)); 11 const [isRunning, setIsRunning] = useState(false); 12 13 // Generate a random update (simulating a dynamic price or statistic) 14 const generateRandomValue = useCallback((currentValue: string): string => { 15 const currentNumber = parseFloat(currentValue.replace(/,/g, '')); 16 const changePercentage = Math.random() * 10; // up to 10% change 17 const isIncrease = Math.random() > 0.5; 18 const changeAmount = (currentNumber * changePercentage) / 100; 19 const newValue = isIncrease 20 ? currentNumber + changeAmount 21 : currentNumber - changeAmount; 22 23 return formatNumber(newValue.toFixed(2)); 24 }, []); 25 26 useEffect(() => { 27 if (!isRunning) return; 28 29 const interval = setInterval(() => { 30 setValue((prev) => generateRandomValue(prev)); 31 }, 1000); 32 33 return () => clearInterval(interval); 34 }, [isRunning, generateRandomValue]); 35 36 return ( 37 <View style={styles.container}> 38 <Text style={styles.header}>React Native Number Flow Example</Text> 39 40 <View style={styles.numberContainer}> 41 <SlotNumberFlow 42 value={value} 43 duration={600} 44 integerDigitSize={30} 45 decimalDigitSize={20} 46 prefixCharacter="$" 47 decimalDigitTranslateY={3} 48 // suffixCharacter="AED" 49 // percentage={true} // Example: turn on to display + / - and % 50 /> 51 </View> 52 53 <TouchableOpacity 54 style={styles.button} 55 onPress={() => setIsRunning((prev) => !prev)} 56 > 57 <Text style={styles.buttonText}> 58 {isRunning ? 'Stop Animation' : 'Start Animation'} 59 </Text> 60 </TouchableOpacity> 61 62 <TouchableOpacity 63 style={styles.button} 64 onPress={() => setValue(formatNumber(10000))} 65 > 66 <Text style={styles.buttonText}>Reset</Text> 67 </TouchableOpacity> 68 </View> 69 ); 70}; 71 72export default App; 73 74const styles = StyleSheet.create({ 75 container: { 76 flex: 1, 77 backgroundColor: '#111827', 78 padding: 20, 79 justifyContent: 'center', 80 }, 81 header: { 82 color: '#fff', 83 fontSize: 20, 84 marginBottom: 20, 85 textAlign: 'center', 86 }, 87 numberContainer: { 88 alignSelf: 'center', 89 marginBottom: 40, 90 }, 91 button: { 92 backgroundColor: '#2563EB', 93 paddingVertical: 12, 94 paddingHorizontal: 16, 95 borderRadius: 8, 96 marginVertical: 5, 97 }, 98 buttonText: { 99 color: '#FFF', 100 textAlign: 'center', 101 fontWeight: '600', 102 }, 103});
Below are the key props you can pass to the SlotNumberFlow
component:
Name | Description | Type | Default |
---|---|---|---|
value | The current numeric value (can be a string or number). | number | string | Required |
percentage | Displays the sign (+/- ) in front of the number and appends % as a suffix. | boolean | false |
duration | Animation duration in milliseconds. | number | 600 |
increaseColor | Text color applied when the value increases. | string | #00FF00 |
decreaseColor | Text color applied when the value decreases. | string | #FF0000 |
defaultColor | Default text color (also used after the animation finishes). | string | #FFFFFF |
textStyle | Additional text style to apply to each digit. | StyleProp<TextStyle> | undefined |
prefixCharacter | Prepends a single character before the value (e.g., $ ). | string | undefined |
suffixCharacter | Appends a single character or string after the value (e.g., AED ). | string | undefined |
customPrefix | Any custom text or symbol to display before the number (often used for more complex prefix scenarios). | string | undefined |
customPrefixTextStyle | Text style for the custom prefix. | StyleProp<TextStyle> | undefined |
customPrefixContainerStyle | Style for the custom prefix container. | StyleProp<ViewStyle> | undefined |
customPrefixColor | Color for the custom prefix text. If not provided, defaults to defaultColor . | string | undefined |
integerDigitSize | Font size for integer digits. | number | 30 |
integerDigitWidth | Digit width for integer digits. If not specified, defaults to a ratio of integerDigitSize * 0.6 . | number | integerDigitSize*0.6 |
integerStepHeight | Vertical stepping height for integer digits (amount of vertical shift in the digit scroll). | number | integerDigitSize*1.2 |
decimalDigitSize | Font size for decimal digits. | number | 20 |
decimalDigitWidth | Digit width for decimal digits. | number | decimalDigitSize*0.6 |
decimalStepHeight | Vertical stepping height for decimal digits. | number | decimalDigitSize*1.2 |
decimalDigitTranslateY | A small vertical offset for decimal digits (useful to align them nicely with integer digits). | number | 0 |
hardwareAccelerationEnabled | Enables shouldRasterizeIOS on iOS and renderToHardwareTextureAndroid on Android for potential performance boosts. | boolean | false |
Below is a more advanced usage example (taken from the library's development demo) where the number updates every second, sometimes with a random increase/decrease, plus the ability to show prefixes, suffixes, decimals, inputting custom values, and more.
1import React, { useCallback, useEffect, useState } from 'react'; 2import { 3 ScrollView, 4 StyleSheet, 5 Text, 6 TextInput, 7 TouchableOpacity, 8 View, 9} from 'react-native'; 10import SlotNumberFlow from '@exodus/react-native-number-flow'; 11 12const formatNumber = (num: string | number): string => { 13 const n = typeof num === 'string' ? parseFloat(num) : num; 14 return new Intl.NumberFormat('en-US').format(n); 15}; 16 17const App: React.FC = () => { 18 const [value, setValue] = useState<string>(() => formatNumber(50000000)); 19 const [isAutoChanging, setIsAutoChanging] = useState(false); 20 const [percentage, setPercentage] = useState(true); 21 const [prefixCharacter, setPrefixCharacter] = useState('$'); 22 const [suffixCharacter, setSuffixCharacter] = useState<string | undefined>(); 23 const [customValue, setCustomValue] = useState(''); 24 25 const generateRandomValue = useCallback((currentValue: string): string => { 26 const currentNumber = parseFloat(currentValue.replace(/,/g, '')); 27 const changePercentage = Math.random() * (10 - 5) + 5; // 5% ~ 10% change 28 const isIncrease = Math.random() > 0.5; 29 const changeAmount = (currentNumber * changePercentage) / 100; 30 const newValue = isIncrease 31 ? currentNumber + changeAmount 32 : currentNumber - changeAmount; 33 return formatNumber(newValue.toFixed(2)); 34 }, []); 35 36 useEffect(() => { 37 if (!isAutoChanging) return; 38 39 const interval = setInterval(() => { 40 setValue((prev) => generateRandomValue(prev)); 41 }, 1000); 42 43 return () => clearInterval(interval); 44 }, [isAutoChanging, generateRandomValue]); 45 46 const handleSetValue = useCallback((raw: string) => { 47 setValue(formatNumber(raw)); 48 }, []); 49 50 return ( 51 <View style={styles.container}> 52 {/* Controls */} 53 <View style={styles.topControls}> 54 <View style={styles.toggleRow}> 55 <TouchableOpacity 56 style={styles.smallToggleButton} 57 onPress={() => setPercentage((prev) => !prev)} 58 > 59 <Text style={styles.smallToggleButtonText}> 60 Percentage: {percentage ? 'ON' : 'OFF'} 61 </Text> 62 </TouchableOpacity> 63 64 <TouchableOpacity 65 style={styles.smallToggleButton} 66 onPress={() => setPrefixCharacter((prev) => (prev ? '' : '$'))} 67 > 68 <Text style={styles.smallToggleButtonText}> 69 {prefixCharacter ? 'Prefix: $' : 'Prefix: None'} 70 </Text> 71 </TouchableOpacity> 72 73 <TouchableOpacity 74 style={styles.smallToggleButton} 75 onPress={() => 76 setSuffixCharacter((prev) => (prev ? undefined : 'AED')) 77 } 78 > 79 <Text style={styles.smallToggleButtonText}> 80 {suffixCharacter ? 'Suffix: AED' : 'Suffix: None'} 81 </Text> 82 </TouchableOpacity> 83 </View> 84 85 <View style={styles.inputRow}> 86 <TextInput 87 style={styles.input} 88 keyboardType="numeric" 89 placeholder="Enter custom value" 90 placeholderTextColor="#666" 91 value={customValue} 92 onChangeText={setCustomValue} 93 /> 94 <TouchableOpacity 95 style={styles.submitButton} 96 onPress={() => customValue && setValue(formatNumber(customValue))} 97 > 98 <Text style={styles.submitButtonText}>Submit</Text> 99 </TouchableOpacity> 100 </View> 101 </View> 102 103 {/* Number Flow */} 104 <ScrollView 105 style={styles.scrollWrapper} 106 contentContainerStyle={styles.scrollContent} 107 > 108 <View style={styles.slotsWrapper}> 109 <View style={styles.slotContainer}> 110 <SlotNumberFlow 111 value={value} 112 duration={1000} 113 percentage={percentage} 114 prefixCharacter={prefixCharacter} 115 suffixCharacter={suffixCharacter} 116 decimalDigitTranslateY={3} 117 integerDigitSize={30} 118 decimalDigitSize={20} 119 integerDigitWidth={18} 120 decimalDigitWidth={12} 121 /> 122 </View> 123 </View> 124 125 {/* Action Buttons */} 126 <View style={styles.buttonContainer}> 127 <TouchableOpacity 128 style={styles.bottomButton} 129 onPress={() => setIsAutoChanging(true)} 130 > 131 <Text style={styles.bottomButtonText}>Start</Text> 132 </TouchableOpacity> 133 134 <TouchableOpacity 135 style={styles.bottomButton} 136 onPress={() => setIsAutoChanging(false)} 137 > 138 <Text style={styles.bottomButtonText}>Stop</Text> 139 </TouchableOpacity> 140 141 <TouchableOpacity 142 style={styles.bottomButton} 143 onPress={() => setValue(formatNumber(50000000))} 144 > 145 <Text style={styles.bottomButtonText}>Reset</Text> 146 </TouchableOpacity> 147 148 <TouchableOpacity 149 style={styles.bottomButton} 150 onPress={() => handleSetValue('-1.23')} 151 > 152 <Text style={styles.bottomButtonText}>-1.23</Text> 153 </TouchableOpacity> 154 155 <TouchableOpacity 156 style={styles.bottomButton} 157 onPress={() => handleSetValue('-52306.57')} 158 > 159 <Text style={styles.bottomButtonText}>-52,306.57</Text> 160 </TouchableOpacity> 161 162 <TouchableOpacity 163 style={styles.bottomButton} 164 onPress={() => handleSetValue('62321865.57')} 165 > 166 <Text style={styles.bottomButtonText}>62,321,865.57</Text> 167 </TouchableOpacity> 168 169 <TouchableOpacity 170 style={styles.bottomButton} 171 onPress={() => handleSetValue('60299.57')} 172 > 173 <Text style={styles.bottomButtonText}>60,299.57</Text> 174 </TouchableOpacity> 175 </View> 176 </ScrollView> 177 </View> 178 ); 179}; 180 181export default App; 182 183/** 184 * Some styling for the example. 185 */ 186const styles = StyleSheet.create({ 187 container: { 188 flex: 1, 189 backgroundColor: '#111827', 190 paddingHorizontal: 20, 191 paddingTop: 100, 192 }, 193 topControls: { 194 marginBottom: 10, 195 backgroundColor: '#1F2937', 196 borderRadius: 20, 197 padding: 15, 198 borderWidth: 1, 199 borderColor: '#374151', 200 shadowColor: '#000', 201 shadowOpacity: 0.2, 202 shadowRadius: 5, 203 elevation: 3, 204 marginTop: 20, 205 }, 206 toggleRow: { 207 flexDirection: 'row', 208 justifyContent: 'center', 209 marginBottom: 15, 210 }, 211 smallToggleButton: { 212 backgroundColor: '#2563EB', 213 borderRadius: 10, 214 paddingVertical: 6, 215 paddingHorizontal: 10, 216 borderWidth: 1, 217 borderColor: '#1E40AF', 218 marginHorizontal: 5, 219 }, 220 smallToggleButtonText: { 221 fontSize: 12, 222 fontWeight: '500', 223 color: '#FFFFFF', 224 textAlign: 'center', 225 }, 226 inputRow: { 227 flexDirection: 'row', 228 alignItems: 'center', 229 justifyContent: 'center', 230 }, 231 input: { 232 flex: 1, 233 backgroundColor: '#1F2937', 234 borderRadius: 8, 235 borderColor: '#374151', 236 borderWidth: 1, 237 padding: 8, 238 fontSize: 14, 239 marginRight: 10, 240 color: '#F3F4F6', 241 }, 242 submitButton: { 243 backgroundColor: '#3B82F6', 244 borderRadius: 8, 245 paddingVertical: 10, 246 paddingHorizontal: 15, 247 shadowColor: '#000', 248 shadowOpacity: 0.3, 249 shadowRadius: 3, 250 elevation: 2, 251 }, 252 submitButtonText: { 253 fontSize: 14, 254 fontWeight: '600', 255 color: '#FFFFFF', 256 textAlign: 'center', 257 }, 258 scrollWrapper: { 259 flex: 1, 260 }, 261 scrollContent: { 262 paddingBottom: 20, 263 }, 264 slotsWrapper: { 265 marginVertical: 10, 266 }, 267 slotContainer: { 268 width: '100%', 269 height: 80, 270 marginVertical: 5, 271 borderRadius: 20, 272 backgroundColor: '#1F2937', 273 alignItems: 'center', 274 justifyContent: 'center', 275 borderWidth: 1, 276 borderColor: '#374151', 277 shadowColor: '#000', 278 shadowOpacity: 0.2, 279 shadowRadius: 5, 280 elevation: 3, 281 padding: 10, 282 }, 283 buttonContainer: { 284 width: '100%', 285 alignItems: 'center', 286 paddingVertical: 10, 287 flexWrap: 'wrap', 288 flexDirection: 'row', 289 justifyContent: 'center', 290 }, 291 bottomButton: { 292 backgroundColor: '#1F2937', 293 paddingVertical: 8, 294 paddingHorizontal: 12, 295 borderRadius: 15, 296 margin: 5, 297 borderWidth: 1, 298 borderColor: '#374151', 299 shadowColor: '#000', 300 shadowOpacity: 0.2, 301 shadowRadius: 3, 302 elevation: 2, 303 }, 304 bottomButtonText: { 305 fontSize: 12, 306 fontWeight: '500', 307 color: '#F3F4F6', 308 textAlign: 'center', 309 }, 310});
Contributions, issues, and feature requests are welcome!
Feel free to check issues page if you want to contribute.
git checkout -b feature-name
.git commit -m "feat: add feature"
.git push origin feature-name
.MIT
Happy Coding! ✨
Made with create-react-native-library
No vulnerabilities found.
No security vulnerabilities found.