Make your files upload-ready.
FormEasy is a small, offline-first React Native app that solves one specific, everyday annoyance: government, job, exam, and KYC portals demand photos and documents in very exact formats — "JPG, 200×230px, 20–50 KB" — and getting there with generic phone tools is trial and error. FormEasy takes a photo, a portal's requirements, and produces a file that's guaranteed to match, entirely on your device.
Upload portals reject files for narrow, mechanical reasons: wrong format, wrong pixel dimensions, file too big or too small. Most people don't know what "compress to 50 KB" means in practice, and generic online converters require uploading personal photos, signatures, and ID documents to an unknown third-party server. FormEasy solves this locally — no upload, no account, no backend.
35-second walkthrough of the golden path: Home dashboard → pick a photo → Passport/Signature requirement presets → Result → Recent Files → Settings. Captured on the iOS Simulator; an MP4 version is also available if you'd rather download it.
| Home dashboard | Select File | Requirements (Passport preset) |
|---|---|---|
![]() | ![]() | ![]() |
| Requirements (Signature preset) | Result | Recent Files | Settings |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
When a request can't be met exactly, the Result screen shows Best Effort with a plain-language explanation instead of silently claiming success — see the compression pipeline section below for how that's decided.
- Prepare Photo — resize, compress, and convert any photo against dimension/size/format requirements you type in
- Prepare Signature — a smart preset (140×60px, 20 KB, JPG) as a starting point; still editable, since portals vary
- Custom Size — type in exactly what a portal asks for, with no hardcoded per-portal presets to go stale
- Before/After comparison with a pass/fail checklist and an honest Ready to Upload / Best Effort / Could Not Meet Requirements status — a quality-loss warning appears before you commit to an unreasonably tight size budget
- Save to Photos and Share the prepared file, output-named
original_formeasy.ext - Fully offline — every step (crop, resize, compress, format conversion) runs on-device
Your files are processed on your device and are never uploaded to a server. This app makes no network calls with file data — there is no backend. Permissions (camera, photo library, save-to-Photos) are requested only at the moment they're needed, with plain-language descriptions of why.
Bare React Native CLI (not Expo) + TypeScript, chosen because the app's core value is native image processing where Expo's managed-workflow constraints wouldn't have added anything material for this specific set of native modules.
| Concern | Library | Why |
|---|---|---|
| Navigation | @react-navigation/native + native-stack | Native transitions, typed routes |
| Image picker | react-native-image-picker | Gallery + camera in one API, returns metadata directly |
| Crop | @react-native-community/image-editor | Native crop-to-rectangle, avoids distorting the subject |
| Resize/compress/convert | @bam.tech/react-native-image-resizer | JPEG quality control, PNG support, exact-dimension "stretch" mode |
| File rename/copy | @dr.pogodin/react-native-fs | Friendly output filenames instead of native UUID temp names |
| Save to Photos | @react-native-camera-roll/camera-roll | The maintained successor to RN core's removed CameraRoll API |
| Share | React Native core Share | No extra dependency needed for a plain share sheet |
| Icons | react-native-svg | A small, curated set of ~12 hand-authored icons rather than a 5000-icon font library |
| State | React useState / local component state | Everything here is screen-local or passed via route params — no app-wide state library earns its keep at this scale |
| Testing | Jest + @testing-library/react-native | Unit tests for the compression/crop/validation logic, component tests for the Requirements form |
Feature-based, not layered — no repository pattern or DI container for a project this size.
src/
navigation/ Typed React Navigation stack + route params
features/
home/ Landing screen
image-picker/ Gallery/camera selection + its native wrapper
requirements/ The dimensions/size/format form + its pure validation logic
result/ Before/after, checklist, Save/Share
recent/ Recent files list, backed by AsyncStorage
settings/ Privacy statement
services/
imageProcessing/ Pure crop-math and quality-search algorithms, plus the
orchestrator that calls the native crop/resize modules
shared/
components/ Button, Card, Chip, TextField
permissions/ Android runtime permission requests (camera, save-to-Photos)
theme/ Design tokens (colors, spacing) + useTheme()
types/ ImageMetadata, ProcessingRequirements, ValidationResult
utils/ formatBytes, fileNaming
services/imageProcessing is deliberately UI-free: the crop-rectangle math and the JPEG quality binary search are plain functions that take data and return data, callable from Jest without a device or simulator. Screens call the orchestrator (pipeline.ts) and render whatever it returns — they don't know or care how compression works.
flowchart LR
A[Selected image] --> B{Dimensions requested?}
B -- no --> E[Resize/compress at original size]
B -- yes, ratio locked --> C[Center-crop to match target ratio]
B -- yes, ratio unlocked --> D[Stretch to exact dimensions]
C --> E
D --> E
E --> F{Format}
F -- JPEG --> G[Binary-search quality<br/>for best fit under max size]
F -- PNG --> H[Single encode<br/>lossless, limited size control]
G --> I[Validate against requirements]
H --> I
I --> J[Before/After + checklist]
The compression step never blindly claims success: if no quality setting lands inside the requested size range without heavy degradation, the Result screen shows Best Effort with a plain-language explanation instead of a false pass.
Prerequisites: Node ≥ 22.11, Xcode (for iOS), CocoaPods, Ruby. Full React Native environment setup: https://reactnative.dev/docs/set-up-your-environment
npm install
cd ios && bundle install && bundle exec pod install &&cd ..
npm start # Metro, in its own terminal
npm run ios # in a second terminalAndroid's toolchain (JDK + Android SDK) is not installed by default in this project's history — set it up once:
brew install --cask temurin17
brew install --cask android-commandlinetools
# Add to ~/.zshrc, then restart your terminal:export ANDROID_HOME="$HOME/Library/Android/sdk"export PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator"
yes | sdkmanager --licenses
sdkmanager "platform-tools""platforms;android-35""build-tools;35.0.0""emulator""system-images;android-35;google_apis;arm64-v8a"
avdmanager create avd -n FormEasy_Pixel -k "system-images;android-35;google_apis;arm64-v8a" -d pixel_7
emulator -avd FormEasy_Pixel
npm run androidOr install Android Studio and let its own SDK Manager set up ANDROID_HOME instead of the command-line tools above.
npx tsc --noEmit # typecheck
npm run lint # ESLint
npm test# Jest: unit + component testsCoverage focuses on the logic most likely to have a subtle bug: the crop-rectangle math, the JPEG quality binary search, requirement validation, and the Requirements form's mode-based presets — all pure functions or component-render assertions, no native code required to run them. A GitHub Actions workflow (.github/workflows/ci.yml) runs typecheck, lint, and tests on every push/PR.
There is no automated end-to-end test that drives the real Simulator/device UI (no Detox/Maestro setup) — the one critical flow (Home → pick a photo → set requirements → Result → Save/Share) should be walked manually before a release; see docs/manual-e2e-checklist.md.
- PDF/document support is not implemented. Reliable cross-platform PDF compression without paid SDKs or a backend was judged too complex for this project's scope; photos and signatures cover the core use case.
- PNG size control is limited — PNG is lossless, so unlike JPEG there's no quality dial to hit a tight size budget. The Result screen surfaces this as a warning rather than silently failing.
- No interactive crop UI. Cropping is automatic (centered, matched to the target aspect ratio) rather than a drag-to-adjust gesture — a deliberate scope cut to avoid pulling in gesture-handling dependencies for a small app.
- Android is unverified on a real device/emulator — every screen, service, and native dependency (image picker, cropper, resizer, camera roll, async storage) is shared, cross-platform code with an Android implementation, and Android-only concerns (runtime
CAMERA/storage permission requests, the Photo Picker backport dependency for API < 30) are wired in — seesrc/shared/permissions/androidPermissions.ts. It just hasn't been built and clicked through on Android, since the toolchain (JDK + Android SDK) isn't installed in this project's history.
AI/ML cleanup, OCR, automatic portal detection, accounts, cloud sync, a backend, community presets, batch processing, and a document scanner are all out of scope for this project by design — see the plan's own "DO NOT BUILD YET" list. PDF support and Android verification are the two items likely to come first if this project continues.







