Building an SSR Scheduling Platform With Vue
Lessons from building a server-side rendered scheduling application for patients and clinical staff — state management, performance, and the challenges of real-time availability.
Patient scheduling is a deceptively complex domain. The UI needs to be simple enough for elderly patients with low digital literacy, fast enough for clinical staff booking back-to-back appointments, and accurate enough that double-bookings are structurally impossible.
Why SSR for a Scheduling Tool
Server-side rendering for a healthcare scheduling tool is the right choice for several reasons:
Search and link sharing. Appointment confirmation pages need to be linkable and printable. SSR makes these pages functional before JavaScript loads.
Performance on constrained devices. A patient using a 5-year-old Android phone on a slow connection should be able to book an appointment. SSR delivers usable content before JavaScript hydrates.
Reliability. An SSR page works — with reduced interactivity — even if JavaScript errors occur. For a healthcare application, that’s a meaningful reliability tier.
State Management for Scheduling Flows
Scheduling flows have multi-step state: appointment type → provider → location → time slot → patient info → confirmation. The state is temporary (session-level) and doesn’t need to persist beyond the browser session.
We used Vuex for this with a single scheduling module. Each step committed to the store, and the confirmation page could render from stored state without re-fetching.
The key insight: don’t normalize scheduling flow state the way you’d normalize persistent data. It’s a form with steps, not a database. A flat object representing the in-progress booking is simpler and easier to reason about than normalized entities.
Real-Time Availability
The hardest technical problem: showing accurate slot availability without displaying slots that are actually taken by another concurrent user.
We used a two-phase approach:
Phase 1: Display “approximately available” slots from a Redis cache updated every 30 seconds. This covers 99% of users who aren’t trying to book simultaneously.
Phase 2: When the user selects a slot, attempt a soft hold (a 5-minute reservation in a lock table). If the hold fails, the slot was just taken — show an “that slot was just booked” message and refresh availability.
This pattern provides a responsive UX without requiring a WebSocket connection for every scheduling session.
The Accessibility Requirements
In a healthcare context, WCAG AA was the floor, not the target. The scheduling flow required:
- Every form step completable by keyboard alone
- All error states announced by screen readers via
aria-liveregions - Session timeout warnings with proper ARIA alerts
- Time zone display in user’s local time, with provider time zone shown for reference
- A “call instead” escape hatch visible at every step
The time zone handling alone was a full sprint. We had providers in three time zones and patients booking from anywhere. The rule: always display in the patient’s local browser time zone, with the provider’s time zone noted if different.
Performance Benchmarks
The scheduling flow pages scored well on Core Web Vitals with SSR:
- LCP under 1.2s on a 3G connection (provider availability SSR’d from Redis cache)
- CLS of 0 (layout reserved for dynamic content slots during SSR)
- FID under 100ms (minimal JS bundle for the scheduling flow)
These aren’t accidental — they require deliberate choices about what to render server-side, what to cache, and what JavaScript to ship.