Tools
Tools: π React Native: Fully Customizable Image Crop Picker (Android + iOS)
2026-02-23
0 views
admin
β¨ Why Another Crop Library? ## Android Screenshot ## iOS Screenshot ## π¦ Installation ## π Complete Working Example ## π― Key Features ## βοΈ Free Style Cropping ## π¨ Fully Custom Header ## π Fully Custom Footer Buttons ## π Native Controls (Optional) ## π¦ Output Format ## π± Real World Use Cases ## π What Makes This Different? ## π Final Thoughts While building a feature recently, I hit a limitation. π¨ Fully customizable header & footer π΅ Circular crop (for profile images) βοΈ Free-style cropping π¦ Base64 + file output π Theme control π± Consistent behavior on Android & iOS Most crop libraries either: react-native-customizable-image-crop-picker Typical problems I faced: β No custom header design β No footer button layout control β Hard to match app branding β Limited icon customization β Poor theme control I wanted something production-ready with deep customization β so I created this package. Hereβs a clean implementation: π΅ Circular Crop (Perfect for Profile Pictures) Let users resize crop area freely. You are not locked into default native UI anymore. If you prefer native system crop toolbar: You get structured output: Which works perfectly for: Instead of forcing native UI, this gives you: β UI-level customization β Native performance β Circular + freestyle crop β Header/Footer full control β Clean base64 output β Android + iOS support If you're building a production React Native app and want: This package is built for that exact use case. If this helped you, consider: π¬ Sharing feedback π Reporting improvements Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. as well , this person and/or CODE_BLOCK: yarn add react-native-customizable-image-crop-picker CODE_BLOCK: yarn add react-native-customizable-image-crop-picker CODE_BLOCK: yarn add react-native-customizable-image-crop-picker CODE_BLOCK: cd ios && pod install CODE_BLOCK: cd ios && pod install CODE_BLOCK: cd ios && pod install COMMAND_BLOCK: import React, { useState } from 'react'; import { Image, Pressable, StatusBar, StyleSheet, Text, View, } from 'react-native'; import { openImageCropPicker } from 'react-native-customizable-image-crop-picker'; const App = () => { const uploadIconUri = require('../../../upload.jpg'); const [image, setImage] = useState(''); const [base64Image, setBase64Image] = useState(''); const commonOptions = { cropWidth: 1, cropHeight: 1, includeBase64: true, compressQuality: 0.8, compressFormat: 'jpeg', circularCrop: true, freeStyleCropEnabled: true, cropGridEnabled: true, showNativeCropControls: false, headerTitle: 'Preview', footerButtonLayout: 'vertical', footerButtonOrder: 'uploadFirst', topLeftControl: 'upload', topRightControl: 'cancel', cancelText: 'Cancel', uploadText: 'Upload', uploadButtonIconUri: uploadIconUri, }; const open = async (source) => { try { const res = await openImageCropPicker({ source, ...commonOptions, }); const uri = res?.path ? `file://${res.path}` : ''; setImage(uri); setBase64Image(res?.base64); } catch (e) { console.log('Crop failed', e); } }; return ( <View style={styles.container}> <StatusBar barStyle="dark-content" /> <Pressable onPress={() => open('camera')}> <Text>Camera</Text> </Pressable> <Pressable onPress={() => open('gallery')}> <Text>Gallery</Text> </Pressable> <Image source={{ uri: image }} style={{ width: 150, height: 150 }} /> <Image source={{ uri: `data:image/jpeg;base64,${base64Image}` }} style={{ width: 150, height: 150 }} /> </View> ); }; export default App; COMMAND_BLOCK: import React, { useState } from 'react'; import { Image, Pressable, StatusBar, StyleSheet, Text, View, } from 'react-native'; import { openImageCropPicker } from 'react-native-customizable-image-crop-picker'; const App = () => { const uploadIconUri = require('../../../upload.jpg'); const [image, setImage] = useState(''); const [base64Image, setBase64Image] = useState(''); const commonOptions = { cropWidth: 1, cropHeight: 1, includeBase64: true, compressQuality: 0.8, compressFormat: 'jpeg', circularCrop: true, freeStyleCropEnabled: true, cropGridEnabled: true, showNativeCropControls: false, headerTitle: 'Preview', footerButtonLayout: 'vertical', footerButtonOrder: 'uploadFirst', topLeftControl: 'upload', topRightControl: 'cancel', cancelText: 'Cancel', uploadText: 'Upload', uploadButtonIconUri: uploadIconUri, }; const open = async (source) => { try { const res = await openImageCropPicker({ source, ...commonOptions, }); const uri = res?.path ? `file://${res.path}` : ''; setImage(uri); setBase64Image(res?.base64); } catch (e) { console.log('Crop failed', e); } }; return ( <View style={styles.container}> <StatusBar barStyle="dark-content" /> <Pressable onPress={() => open('camera')}> <Text>Camera</Text> </Pressable> <Pressable onPress={() => open('gallery')}> <Text>Gallery</Text> </Pressable> <Image source={{ uri: image }} style={{ width: 150, height: 150 }} /> <Image source={{ uri: `data:image/jpeg;base64,${base64Image}` }} style={{ width: 150, height: 150 }} /> </View> ); }; export default App; COMMAND_BLOCK: import React, { useState } from 'react'; import { Image, Pressable, StatusBar, StyleSheet, Text, View, } from 'react-native'; import { openImageCropPicker } from 'react-native-customizable-image-crop-picker'; const App = () => { const uploadIconUri = require('../../../upload.jpg'); const [image, setImage] = useState(''); const [base64Image, setBase64Image] = useState(''); const commonOptions = { cropWidth: 1, cropHeight: 1, includeBase64: true, compressQuality: 0.8, compressFormat: 'jpeg', circularCrop: true, freeStyleCropEnabled: true, cropGridEnabled: true, showNativeCropControls: false, headerTitle: 'Preview', footerButtonLayout: 'vertical', footerButtonOrder: 'uploadFirst', topLeftControl: 'upload', topRightControl: 'cancel', cancelText: 'Cancel', uploadText: 'Upload', uploadButtonIconUri: uploadIconUri, }; const open = async (source) => { try { const res = await openImageCropPicker({ source, ...commonOptions, }); const uri = res?.path ? `file://${res.path}` : ''; setImage(uri); setBase64Image(res?.base64); } catch (e) { console.log('Crop failed', e); } }; return ( <View style={styles.container}> <StatusBar barStyle="dark-content" /> <Pressable onPress={() => open('camera')}> <Text>Camera</Text> </Pressable> <Pressable onPress={() => open('gallery')}> <Text>Gallery</Text> </Pressable> <Image source={{ uri: image }} style={{ width: 150, height: 150 }} /> <Image source={{ uri: `data:image/jpeg;base64,${base64Image}` }} style={{ width: 150, height: 150 }} /> </View> ); }; export default App; CODE_BLOCK: circularCrop: true CODE_BLOCK: circularCrop: true CODE_BLOCK: circularCrop: true CODE_BLOCK: freeStyleCropEnabled: true CODE_BLOCK: freeStyleCropEnabled: true CODE_BLOCK: freeStyleCropEnabled: true CODE_BLOCK: showNativeCropControls: true controlsPlacement: 'bottom' CODE_BLOCK: showNativeCropControls: true controlsPlacement: 'bottom' CODE_BLOCK: showNativeCropControls: true controlsPlacement: 'bottom' CODE_BLOCK: { path: string, width: number, height: number, mime: string, base64?: string } CODE_BLOCK: { path: string, width: number, height: number, mime: string, base64?: string } CODE_BLOCK: { path: string, width: number, height: number, mime: string, base64?: string } - Donβt allow UI customization - Lock you into native toolbar - Or donβt give proper base64 support - Background color - Font styling - Layout (horizontal / vertical) - Button order - Icons (local or remote) - Border radius - API uploads - Firebase Storage - Multipart forms - Instant preview - Profile image upload - KYC document capture - E-commerce product listing - Social media post creation - Avatar builders - Custom design tools - Full branding control - No native UI limitations - Proper base64 support
toolsutilitiessecurity toolsreactnativefullycustomizableimagepickerandroid