Work Workout 24/7
A gym site the owner can actually run
The original WordPress site needed a developer for every price change. The rebuild models the business in the CMS instead of in pages — adding a town means adding a document, and the route, the navigation, and the metadata follow from it.
- Role
- Sole developer — content model, build, and ongoing maintenance
- Stack
- Next.js 16 · React 19 · TypeScript · Tailwind CSS · Sanity · Upstash Redis · Resend · Vercel
- Links
- Live site · Source on GitHub
A content model, not a page builder
Locations, membership plans, and FAQ entries are modelled as document types sharing a common SEO field object, rather than as free-form pages. One dynamic route renders any location from its slug, so Osakis and Wells are the same code path and cannot drift apart over time.
Tag-scoped revalidation instead of rebuilds
A CMS webhook verifies its payload signature and purges only the cache tags belonging to documents that actually changed. A price edit is live in seconds, and nothing unrelated is invalidated — the site stays static-fast without the owner waiting on a full rebuild.
A contact form that fails in the right direction
The form pairs a honeypot with a Redis-backed sliding-window rate limiter. Serverless functions do not share memory, so in-process limiting would have been decorative — hence an external store.
The limiter fails open by design. If Redis is unreachable, submissions still go through: silently dropping a real enquiry from a paying customer is a worse outcome than briefly allowing spam, and the honeypot still applies.