Thanks for considering a contribution to the spoo.me frontend. Every fix helps.
This repo is the web app only. Anything about the API, redirects, database or self-hosting the backend belongs in spoo-me/spoo.
You do not need a backend to work on this. The repo ships a mock one.
# Fork and clone
git clone https://github.com/YOUR_USERNAME/frontend.git
cd frontend
npm install
npm run dev:mockVisit http://localhost:3001. Any email and password signs in, any 6 digits pass the OTP step, and the workspace arrives pre-filled with links, domains, webhooks, keys and click history.
The dataset is seeded from a fixed PRNG in app/api/mock/[...path]/seed.ts, so the numbers are identical on every restart. State lives in the dev-server process: restart, or hit GET /api/mock/reset, to start over.
Use npm run dev instead if you want the real API. It expects a spoo.me backend on http://localhost:8000; override with SPOO_API_URL. See the README for the optional environment variables.
Note
dev and dev:mock use separate build directories (.next and .next-mock) and separate ports, so you can run both at once.
- Branch off
main:git checkout -b feat/my-change - Make the change
- Run the checks below
- Push and open a pull request
.github/workflows/ci.yml runs these on every pull request, in this order. Run them locally first.
npx biome ci . # formatting + Tailwind class order (npm run format writes fixes)
npm run typecheck # tsc --noEmit
npm test # vitest
npm run build # next buildNotes on each:
- Biome, not Prettier.
npm run formatisbiome check --write .. It also sorts Tailwind classes onclassName,cn()andcva(), which is a lint error when wrong, not a preference. npm run buildis the real smoke test. Server and client boundary mistakes passtscand then explode here. Do not skip it.npm run lint(eslint) is not yet a CI gate. There is a backlog of react-hooks errors in vendoredcomponents/ui. New code should still be clean.- Tests are Vitest in a node environment. Only
lib/**/*.test.ts,hooks/**/*.test.tsandproxy.test.tsare collected; seevitest.config.ts.
- TypeScript strict. No
anyescape hatches in new code. - Comments explain why. The codebase leans on block comments at the top of a module stating the constraint it exists to satisfy. Match that. Do not narrate what the code already says.
- Every
NEXT_PUBLIC_*switch is declared inlib/flags.ts, with what it hides and when it dies. No other module readsprocess.env.NEXT_PUBLIC_*directly. - Wire types live in
lib/api/, one module per backend surface. If you change a wire shape, change the mock inapp/api/mock/in the same commit so the two never drift. - UI primitives come from the registry.
npx shadcn@latest add <name>for shadcn,npx shadcn@latest add "https://magicui.design/r/<name>"for Magic UI,npx shadcn@latest add "@aceternity/<name>"for Aceternity. Do not hand-copy them.
- Keep the PR focused on one change
- All CI checks must pass
- Screenshots or a short clip for anything visual, in both themes if the change touches color
- Conventional commit messages:
feat:,fix:,chore:,refactor:,docs: - Update the README if you change how the app is run or configured
- Discord for real-time help
- GitHub Issues for bugs and feature requests
- Documentation for API details
AGPL-3.0. See LICENSE. By contributing you agree your work is licensed the same way.