A full-stack parcel tracking web application that lets users track shipments in real-time across multiple couriers worldwide, powered by the TrackingMore API.
Live Demo: parceltrackersystem.netlify.app
Backend API: parcel-tracking-system-production.up.railway.app
Repository: github.com/Muhammad-Raheel04/parcel-tracking-system
Note: The live demo may be unavailable as the TrackingMore API free tier key expires after 2 weeks. To see the app in action, watch the demo video below.
- Real-time parcel tracking — enter any tracking number and get live status updates
- Multi-courier support — hundreds of couriers worldwide (Pakistan Post, DHL, FedEx, UPS, and more)
- Full tracking history — visual timeline of every checkpoint your parcel has passed through
- Location & event details — see location, timestamp, and status for each event
- Fast & responsive — works seamlessly on mobile, tablet, and desktop
- Secure — API key is kept server-side only; the frontend never exposes credentials
| Technology | Purpose |
|---|---|
| React 18 | UI framework |
| Vite | Build tool & dev server |
| Tailwind CSS | Utility-first styling |
| Lucide React | Icon library |
| Technology | Purpose |
|---|---|
| Node.js | Runtime |
| Express.js | Web framework |
| TrackingMore API | Shipment data provider |
| dotenv | Environment variable management |
| CORS | Cross-origin request handling |
| Service | Purpose |
|---|---|
| Netlify | Frontend hosting |
| Railway | Backend hosting |
parcel-tracking-system/
│
├── frontend/
│ ├── src/
│ │ ├── api/
│ │ │ └── trackingApi.js # Thin HTTP client (no API keys)
│ │ ├── hooks/
│ │ │ ├── useTracking.js # Tracking state — owned by App
│ │ │ └── useCouriers.js # Couriers state — owned by TrackingForm
│ │ ├── components/
│ │ │ ├── Navbar.jsx
│ │ │ ├── Footer.jsx
│ │ │ ├── TrackingForm.jsx # Form + courier select
│ │ │ ├── TrackingResult.jsx # Summary card
│ │ │ └── TrackingHistory.jsx # Timeline of events
│ │ ├── App.jsx # Minimal — layout + wiring only
│ │ └── main.jsx
│ └── .env # VITE_API_BASE_URL
│
└── backend/
├── controllers/
│ ├── couriersController.js # GET /api/v1/couriers
│ └── trackingController.js # POST /api/v1/trackings
├── routes/
│ ├── couriersRoute.js
│ └── trackingRoute.js
├── server.js
└── .env # TRACKING_API_KEY, PORT, FRONTEND_URL
- Node.js v18+
- npm or yarn
- A free TrackingMore API key
git clone https://github.com/Muhammad-Raheel04/parcel-tracking-system.git
cd parcel-tracking-systemcd backend
npm installCreate a .env file inside backend/:
PORT=5000
TRACKING_API_KEY=your_trackingmore_api_key_here
FRONTEND_URL=http://localhost:5173Start the server:
node server.jsBackend runs at http://localhost:5000.
cd frontend
npm installCreate a .env file inside frontend/:
VITE_API_BASE_URL=http://localhost:5000Start the dev server:
npm run devApp runs at http://localhost:5173.
Base URL: https://parcel-tracking-system-production.up.railway.app
GET /api/v1/couriers
Response:
{
"success": true,
"message": "successfully fetched list of couriers",
"data": [
{ "courier_code": "pakistan-post", "courier_name": "Pakistan Post" },
{ "courier_code": "dhl", "courier_name": "DHL" }
]
}POST /api/v1/trackings
Request Body:
{
"tracking_number": "EA123456789PK",
"courier_code": "pakistan-post"
}Response:
{
"trackingNumber": "EA123456789PK",
"courierCode": "pakistan-post",
"status": "in transit",
"estimateDeliveryTime": "2026-03-25",
"originCountry": "Pakistan",
"destinationCountry": "Pakistan",
"lastEvent": "Parcel received at sorting center",
"events": [
{
"statusDescription": "Parcel received at sorting center",
"eventTime": "2026-03-21 10:30:00",
"address": "Karachi GPO",
"details": "in_transit"
}
]
}The app follows a strict "fetch where first needed, pass down only if required" pattern:
useCouriers— lives insideTrackingFormonly. Couriers are never needed anywhere else, so the hook stays local and is never hoisted.useTracking— lives inApp, because bothTrackingResultandTrackingHistoryconsume the same tracking data. State is hoisted to the lowest common ancestor and passed as props.
The TrackingMore API key is never exposed to the browser. All requests to TrackingMore go through the Express backend, which reads the key from environment variables. The frontend only ever calls your own backend.
trackingApi.js is a pure HTTP client — no caching, no normalization, no business logic. Data normalization happens in the backend controllers, keeping the frontend fully decoupled from TrackingMore's response shape.
- Push your code to GitHub
- Connect the repo on Netlify
- Set build settings:
- Build command:
npm run build - Publish directory:
dist
- Build command:
- Add environment variable in Netlify dashboard:
VITE_API_BASE_URL= your Railway backend URL
- Connect the repo on Railway
- Set the root directory to
/backend - Add environment variables in Railway dashboard:
PORT=5000TRACKING_API_KEY= your TrackingMore keyFRONTEND_URL= your Netlify frontend URL
Contributions are welcome!
- Fork the repository
- Create a branch:
git checkout -b feature/your-feature-name - Commit your changes:
git commit -m 'Add some feature' - Push to the branch:
git push origin feature/your-feature-name - Open a Pull Request
This project is open source and available under the MIT License.
Muhammad Raheel
GitHub: @Muhammad-Raheel04
Built with ❤️ using React, Express, and TrackingMore API