A full-stack URL shortener with JWT accounts, a link-management dashboard and per-link click analytics.
This repo is the React frontend; the REST API lives in url-shortener-api.
Or log in manually with demo@urlshortener.dev / demo1234
β³ The backend runs on Render's free tier. A keep-alive job pings it every 14 minutes, so it's normally awake; if it isn't, the app shows a "waking up" notice and the first request can take up to a minute.
| π Shorten links | Random 7-character codes, or your own custom alias (3β20 characters) |
| π Dashboard | Search, sort by date / clicks / URL, filter by minimum clicks, pagination, live totals |
| βοΈ Edit & delete | Change a link's destination or alias; delete with a confirmation dialog |
| π Analytics | Lifetime clicks, clicks per day, a click-event log, and date-range filtering |
| π Accounts | Register / log in with JWT; every user only ever sees their own links |
| π One-click demo | A shared demo account with sample links β visible from the navbar, home, login and register pages |
| π Resilient UX | "Waking up" notice for cold starts, clear errors when the API is unreachable, toast on expired sessions, no accidental logouts on slow networks |
| π± Responsive | Works on phones and desktops |
flowchart LR
user["π€ Visitor"] -->|"uses"| web["βοΈ Shortly frontend<br/>React + Vite Β· Vercel"]
web -->|"HTTPS Β· JSON Β· JWT"| api["π’ REST API<br/>Node.js + Express Β· Render"]
clicker["π Anyone opening a short link"] -->|"GET /:code"| api
api -->|"302 redirect + click recorded"| clicker
api --> db[("π MongoDB Atlas<br/>users Β· urls Β· clicks")]
cron["β° GitHub Actions keep-alive<br/>every 14 min"] -.->|"GET /health/db"| api
| Part | Repo | Hosting |
|---|---|---|
| Frontend (this repo) | url-shortener-frontend |
Vercel |
| REST API | url-shortener-api |
Render (free tier) + MongoDB Atlas |
| Click analytics pipeline (Kafka β ClickHouse) | clickstream-pipeline |
Docker / Codespaces |
| First version (Express + Redis cache) | url-shortener |
β |
- Auth: the JWT is kept in
localStorage; an Axios request interceptor addsAuthorization: Bearer β¦to every call, and a response interceptor turns any401into a clean logout plus a "session expired" toast. - Protected routes:
/dashboardand/analytics/:coderedirect to/login(remembering where you were going); the login page offers the one-click demo, so nobody gets stuck. - Session restore: on load the app calls
GET /api/auth/me. Only a real401clears the session β a timeout while the free server wakes up keeps you signed in. - Demo account:
POST /api/auth/demoreturns a token for a shared account. The API keeps it to its newest 20 links and re-creates sample links if it's ever emptied.
React 19 Β· Vite 8 Β· React Router 7 Β· Axios Β· lucide-react icons Β· a hand-written CSS design system (no UI framework) Β· ESLint Β· Vercel
git clone https://github.com/ARYAN10227/url-shortener-frontend.git
cd url-shortener-frontend
npm install
cp .env.example .env # set VITE_API_URL=http://localhost:3000 to use a local API
npm run dev # β http://localhost:5173| Script | What it does |
|---|---|
npm run dev |
Dev server with hot reload |
npm run build |
Production build into dist/ |
npm run preview |
Serve the production build locally |
npm run lint |
ESLint |
| Variable | Default | Purpose |
|---|---|---|
VITE_API_URL |
https://url-shortener-api-h1dj.onrender.com |
Base URL of the REST API |
src/
βββ pages/ Landing, Login, Register, Dashboard, Analytics
βββ components/ Navbar, Footer, Edit/Delete/Analytics modals, DemoLoginButton,
β ServerWakeNotice, ProtectedRoute, Toast
βββ context/ AuthProvider (session, login, demo, logout), ToastProvider
βββ services/ api.js β Axios client, token storage, every API call
βββ index.css Design tokens and components
- Hosted on Vercel; every push to
maindeploys automatically, and pull requests get preview URLs. vercel.jsonrewrites every path toindex.html, so client-side routes survive a page refresh.- Deployment Protection is off for production β otherwise visitors are sent to a Vercel login page.





