1. 23:04 09/10/2026
## Introduction
Building a file upload feature that works seamlessly across web and mobile platforms can be challenging. By extracting common logic into reusable hooks and centralizing state with the Context API, you can keep your codebase DRY while delivering a consistent user experience. This article walks through a practical pattern that combines custom hooks, global state management, and React Query to handle validation, progress tracking, and server integration in a scalable way.
## Core Architecture
The upload workflow is split into three logical layers:
1. **UI Layer** – Component tree that renders the drag‑and‑drop zone, file list, and progress indicators.
2. **Business Logic Layer** – Custom hooks that encapsulate file handling, validation, and upload requests.
3. **State Layer** – A single Context provider that shares upload status across React web and React Native.
Separating concerns in this way makes it easy to test each part independently and to reuse the hooks in other projects.
## Reusable Hooks
A set of focused hooks eliminates duplicate code:
- **useFileInput** – Manages file selection from both input elements and native pickers, returning an array of `File` objects.
- **useUploadMutation** – Wraps `React Query`'s `useMutation` to perform the actual HTTP request, handling success/failure callbacks.
- **useUploadProgress** – Uses the `progressEvent` from `XMLHttpRequest` (or the Fetch API) to update a progress percentage, exposing a `progress` state.
Each hook is independent, can be imported wherever needed, and can be composed to build more complex behavior.
## Global State with Context API
Because both React web and React Native need to show the same upload queue and overall status, a single `UploadContext` is created. The context holds:
- `queue`: an array of upload jobs with `id`, `file`, `status`, `progress`, and `error`.
- `addToQueue`, `updateJob`, `removeJob`: actions to manipulate the queue.
All UI components consume this context, ensuring a single source of truth for the upload process.
## Server Synchronization with React Query
React Query simplifies server state by caching, background refetching, and error handling. The `useUploadMutation` hook uses `useMutation` to trigger uploads and automatically updates the queue via the context when the mutation succeeds or fails.
## Validation and Error Handling
Validation rules are applied early, inside the custom hooks, so invalid files never reach the server. Common checks include file size, MIME type, and duplicate names. Errors are stored in the queue’s `error` field and displayed inline, giving users clear feedback.
## UI/UX Best Practices
- **Drag‑and‑Drop**: Use HTML5 native drag events on web and `react-native-gesture-handler` on mobile.
- **Progress Feedback**: Show per‑file progress bars and an overall upload percentage.
- **Accessibility**: Attach appropriate ARIA labels and provide keyboard navigation for the upload zone.
- **Responsive Design**: Keep the UI consistent by using a shared component library (e.g., styled‑components with native support).
## Scaling Considerations
- **Batch Uploads**: Extend the queue to support parallel uploads with a configurable concurrency limit.
- **Server‑Side Resumption**: Include an `X-Upload-ID` header to allow the server to resume interrupted uploads.
- **Optimistic Updates**: Leverage React Query’s optimistic updates to improve perceived performance.
## Conclusion
By abstracting file handling into reusable hooks and centralizing state with a Context provider, you can build a file upload system that feels native on both web and mobile while keeping the code maintainable and testable. The pattern described here works well with React Query for server sync and can be extended to support advanced features like chunked uploads or cloud storage integration.