The web app behind spoo.me: landing, auth, dashboard and analytics π₯οΈ
β‘ Introduction π§© What's in here π Getting Started ποΈ Project Layout π€ Contributing
spoo.me is a free, open-source link management platform: short links, custom slugs, emoji slugs, password protection, link lifespans and deep click analytics, all behind a documented API.
This repository is the frontend. Everything a browser touches lives here: the marketing site, sign-in and onboarding, the dashboard, the public per-link stats and preview pages, and the error pages the edge composes. The backend (FastAPI, MongoDB, Redis) lives in spoo-me/spoo.
| Live app π | Backend repo π | API docs π οΈ |
|---|
The analytics board: a drag-and-resize widget grid over the same click data the API exposes.
More screens
Overview: the daily briefing, today's numbers, what needs attention, what is hot right now.
Links: the workspace table, filterable, with per-link settings in a side sheet.
Landing: shorten without an account, straight from the hero.
Note
Every screenshot above is the app running against the built-in mock backend (npm run dev:mock), on seeded data. No real accounts or links.
Landing- hero shortener, feature sections, apps and SDKs directory, testimonials, pricing πAuth- sign in, sign up with email OTP, OAuth (Google, GitHub, Discord), password reset πOnboarding- a branching wizard that ends with a link you actually made π§Dashboard- overview, links, analytics, domains, connected apps, webhooks, API keys, account settings πAnalytics board- a resizable widget grid: time series, breakdowns, world map, treemap, radar, heatmap πLink editing- slugs and emoji slugs, passwords, expiry, click caps, bot blocking, geo rules, A/B variants, meta tags π§Public pages- per-link stats at/stats/{code}and the safety preview at/{code}+πError pages- 404 / 410 / 429 / 451 / 5xx, composed at the edge from backend statuses π§Intake- abuse reporting (single and bulk) and contact, both captcha-gated for anonymous senders π‘οΈMock backend- the whole app running on a seeded in-memory dataset, no services required π§ͺ
- Node.js 22 or newer π©
git clone https://github.com/spoo-me/frontend.git
cd frontend
npm installnpm run dev:mockOpen http://localhost:3001. This is the fastest way in and the way to develop most UI.
SPOO_MOCK=1 points the same-origin proxy at in-repo mock handlers instead of the real API, so the real pages run the real flow against canned responses. The dataset is seeded from a fixed PRNG, so the numbers are the same on every restart.
- Any email and password signs in, any 6 digits pass the OTP step
- The workspace comes pre-filled with links, domains, webhooks, keys and click history
- State lives in the dev-server process. Restart, or hit
GET /api/mock/reset, to start over
Expand this to run against the real backend
npm run devOpen http://localhost:3000. This expects a spoo.me backend on http://localhost:8000 (the default from the backend repo's docker-compose). Override with SPOO_API_URL.
/auth/*, /oauth/* and /api/v1/* are rewritten to that origin from the same Next server, which keeps the HttpOnly auth cookies first-party and avoids CORS entirely.
Every one of these is optional and every one degrades to a no-op when unset. lib/flags.ts is the single registry, so one read of that file lists every switch.
SPOO_API_URL=http://localhost:8000 # backend origin for the proxy
NEXT_PUBLIC_PRICING= # 1 shows the /pricing surface
NEXT_PUBLIC_HCAPTCHA_SITEKEY= # unset skips the captcha step entirely
NEXT_PUBLIC_POSTHOG_KEY= # unset disables product analytics
NEXT_PUBLIC_CLARITY_ID= # unset disables session replay
NEXT_PUBLIC_SENTRY_DSN= # unset disables browser error reporting[!IMPORTANT]
NEXT_PUBLIC_*values are inlined into the client bundle at build time, not read at runtime. In Docker they must arrive as build args, and changing one means a rebuild.
# The backend origin is compiled into the proxy at build time (default https://spoo.me).
docker build --build-arg SPOO_API_URL=http://host.docker.internal:8000 -t spoo-frontend .
docker run -p 3000:3000 spoo-frontendThe image is a multi-stage build ending on Next's standalone output: no node_modules at runtime, non-root user, a health endpoint at /api/health.
app/
page.tsx landing
(auth)/ login, signup, forgot-password
onboarding/ welcome, verify, path, then link/domain/api/apps/claim, then recap
dashboard/ overview, links, analytics, domains, apps, webhooks,
developer (API keys), settings
stats/[code]/ public per-link stats
preview/[code]/ safety preview, served at the public URL /{code}+
error-pages/[status]/ edge-composed 404 / 410 / 429 / 451 / 5xx
report/, contact/ abuse and support intake
apps/, pricing/ ecosystem directory, plans
legal/, privacy/, terms/, about/, testimonials/
api/mock/[...path]/ the mock backend (SPOO_MOCK=1 only)
api/health/ container health probe
components/
sections/ landing sections
dashboard/ dashboard shell, links UI, analytics widgets
onboarding/, auth/, stats-public/, preview/, errors/, report/
layout/, shared/, icons/
ui/ shadcn + Magic UI + Aceternity primitives
lib/
api/ typed clients, one module per backend surface
flags.ts every NEXT_PUBLIC_* switch, and what it hides
site-config.ts site metadata, nav, footer, public stats
apps-data.ts connected apps and SDK registry
hooks/ shared React hooks
proxy.ts auth gate for /, /dashboard/*, /onboarding/*
next.config.mjs rewrites: API proxy, /{code}+, /_error/{status}
public/ brand assets, geo topojson, security.txt
- Next.js 16 App Router, Turbopack, standalone output
- React 19 and TypeScript in strict mode
- Tailwind CSS v4 with shadcn/ui (radix-nova) primitives
- TanStack Query for server state, nuqs for URL state
- Recharts and react-grid-layout for the analytics board
- Motion for animation, next-themes for dark mode
- Biome for formatting and lint, Vitest for tests
Contributions are always welcome! π
- Read the contribution guidelines first, they cover the checks CI runs
- Bugs and ideas go through GitHub issues
- Then open a pull request
npm run format # biome, writes fixes
npm run typecheck # tsc --noEmit
npm test # vitest
npm run build # the real smoke testImportant
For support or questions, reach out at βοΈ support@spoo.me. For security reports, see SECURITY.md.

