After finally making all the pages in the Freight Tracker frontend look more polished — with a cleaner layout, better navigation, nicer styling, and a responsive design — I thought I was done. But I wasn't. There was still a lot to improve under the hood.
Here's a breakdown of everything I worked on after the UI was "done," and what I learned along the way:
🔄 Making the App Feel More Dynamic with Zustand and WebSockets
I had already integrated Zustand in Phase 2, but now I made it more useful. Instead of hitting the API every time, I could cache and reuse shipments in the Zustand store. That made the app snappier.
Then I added WebSocket support to make live updates possible — so if a shipment gets updated in real-time (e.g., status changes to "DELIVERED"), it reflects instantly in the UI without the user refreshing the page. This gave the app a much more modern feel.
What I learned:
- Zustand is great for global state without the boilerplate of Redux
-
useShipmentStore.getState()
gives access outside components - WebSockets with STOMP and SockJS were already set up in the backend, so I just needed to connect and subscribe to the right topic
- Having WebSockets + Zustand together means my data stays fresh and fast
🗺️ Making the Map More Realistic
The Map Dashboard looked okay, but loading was slow. Why? Because I was calling the Google Geocode API for every city — every time.
So I added a geocode cache using localStorage
. Now if a city was already looked up before, we reuse the coordinates. This made route rendering way faster.
I also:
- Gave each route a different color based on shipment status
- Added loading indicators while geocoding
- Made the map update live when new shipments are added via WebSocket
⚠️ Fixing the Refresh Issue on Netlify
One of the most annoying bugs I ran into: when I refreshed a route like /shipments/2
, I got a "page not found" error. Turns out this is a common issue with React apps deployed on Netlify.
The fix? A simple _redirects
file in the public/
folder:
/* /index.html 200
That tells Netlify to send all routes to index.html
so React Router can do its thing.
🕒 Preventing Backend Sleep with GitHub Actions
After deploying the backend to Render, I noticed that the free tier web service would go to sleep after a few minutes of inactivity. This meant that every time I revisited the app after a while, the backend had to "wake up," causing slow response times or even temporary unavailability.
To fix this, I added a lightweight GitHub Actions workflow in my backend repository that pings the backend url every 10 minutes — just enough to keep it alive without overwhelming it.
Here's what the workflow looks like:
yaml
# .github/workflows/keep-alive.yml
name: Keep Render Backend Alive
on:
schedule:
- cron: '*/10 * * * *' # runs every 10 minutes
workflow_dispatch: # allows manual triggering
jobs:
ping:
runs-on: ubuntu-latest
steps:
- name: Curl the backend
run: curl --s https://freight-tracker.onrender.com/api/shipments
Once pushed to the main branch, it showed up in the Actions tab on GitHub. Now I never have to worry about the backend sleeping on me. 🚀
⚠️ Note: GitHub Actions schedules are “best-effort” and may not always run exactly on time. If you want a more consistent keep-alive mechanism, consider using a free external ping service like UptimeRobot
or cron-job.org
## 🌍 Frontend + Backend Deployment
The frontend was deployed on **Netlify** and the backend (Spring Boot) was containerized and deployed on **Render** using Docker.
Key steps I learned:
* Backend needs CORS configured correctly (e.g. allow frontend URL)
* Set up a PostgreSQL DB on Render and connect via environment variables
* Render's default `.mvnw` build failed, so I switched to Docker and wrote a `Dockerfile` instead
* Frontend needed environment variable for backend API (`VITE_API_URL`)
* I had to commit my real `application.properties`, not `application.properties.example`, with placeholders like `${DB_URL}` — and set those secrets in Render
Here's the live app: https://freighttracker.netlify.app/
Top comments (0)