Skip to main content

Case study · Vital Ice

A site that drives a studio, not a SaaS.

A recovery wellness studio at 2400 Chestnut Street, Marina District. Three co-founders, six service lines, a public site that explains each plainly and feeds Mindbody for everything else.

Role
Designer & developer
Timeline
2025 — present
Stack
Next.js 16 · React 19 · TypeScript · Sanity 4 · Mindbody · Resend · Sentry
Status
Live · ~12 months of active development · Visit live site ↗
Outcome
Stephen, Sean, and Barry update service copy, images, and testimonials themselves in Sanity — no developer needed for content changes — while every booking still flows through Mindbody, the system they already run the studio on.
§ I

The job

Stephen, Sean, and Barry were opening a new recovery studio in the Marina with six service lines — cold plunge, infrared sauna, traditional sauna, red light therapy, percussion massage, compression boots. Their own line was “elite recovery simple, social, and within reach,” built for the early risers, the post-work plungers, the weekend warriors.

They needed a public site that explained each of the six services in plain language, drove bookings into Mindbody, and stayed editable as the offering shifted. Not a brochure, not a SaaS — a working front door.

§ II

Constraints

Mindbody was already the source of truth for class schedules, memberships, leads, and the waitlist. The website was not going to replace it — it was going to feed it. That single decision shaped most of the architecture. Every form on the site eventually writes to a Mindbody endpoint. The class schedule on the booking page is pulled live from Mindbody on each request. No content can drift between site and booking system because the booking system always wins.

The team needed to update marketing copy, service descriptions, hero images, and testimonials without re-engaging me. Sanity was the right tool — the content model maps to actual editable surfaces, not just fields in a CMS for the sake of having a CMS.

§ III

What I built

Next.js 16 App Router, React 19, TypeScript. Plain CSS with BEM. A pnpm + Turbo monorepo with three apps — the public site at vitalicesf.com, the Sanity Studio for content editors, and a small internal admin interface — plus five shared packages: UI components, a business-config single source of truth (hours, address, brand colors), a typed Mindbody SDK wrapper, transactional email templates via Resend and React Email, and shared test utilities.

The Mindbody integration runs through that typed wrapper. Class schedule reads are server-rendered with a short revalidation window. Form posts — newsletter, contact, waitlist, membership inquiry, lead — submit to Mindbody, with Resend firing a confirmation email. The email is non-fatal: a flaky email provider cannot break a signup, only delay a follow-up.

Forms are migrating to React 19 Server Actions with Zod schemas as the single source of truth for client and server validation. Newsletter is on the new pattern. Contact, waitlist, membership inquiry, and lead are next. The migration is documented in the repo so it can be picked up without rediscovering the constraints.

Errors land in Sentry tagged by action and stage. A failure reads as “newsletter signup, Mindbody add_client step” rather than “form broke.”

§ IV

What I deliberately did not build

The website’s job is to drive to Mindbody. Anything that did not serve that was deferred. The list below is not a backlog — it is a set of choices.

  • No on-site payments — Mindbody handles billing.
  • No marketing-automation pipeline — Resend handles transactional, Mindbody handles CRM messaging.
  • No community forum, no mobile app, no live chat.
  • No homegrown video hosting; embedded references where needed.
§ V

What the team does without me

Service descriptions, hero images, testimonials, blog posts, FAQ entries — all live in Sanity Studio. Edits revalidate the relevant pages within an hour. The studio team uses it directly; no developer needed for copy or imagery.

Class schedules: nothing to maintain on the website at all. They live in Mindbody where the studio team already manages them, and the website pulls live on each request.

Lead pipeline: every form on the site feeds Mindbody. The team sees inquiries in the system they already use, alongside members and class registrations.

A CLAUDE.md in the repo documents the forms pattern and the constraints behind it, so a future engineer — or me, six months from now — can extend it without re-deriving the architecture.

Vital Ice has been live for about a year. The codebase has stayed small on purpose. The team runs it.