Multi-Tenant Salon Management Platform
NovaHair is a production-ready, enterprise-grade monorepo solution for beauty salon management. It combines a Progressive Web App admin panel, an embeddable booking widget, and customizable landing pages into a cohesive, type-safe ecosystem.
Built with React 19, TanStack Router, TanStack Query, and following Clean Architecture principles, this project demonstrates advanced monorepo management, performance optimization, and scalable frontend architecture.

Project Overview
NovaHair is a multi-tenant SaaS platform designed to streamline salon operations. The system consists of three main applications:
- π― Admin Panel - Full-featured PWA for salon management
- π Booking System - Embeddable widget for customer appointments
- π Landing Pages - Customizable templates for salon websites
All applications share a common design system and business logic through a pnpm workspace monorepo, ensuring consistency and reducing code duplication by ~40%.
Key Achievements
- β‘ Sub-second load times - Achieved < 800ms FCP through code splitting and SSR
- π¦ 40% code reduction - Shared packages eliminate duplication across apps
- π Real-time sync - Optimistic updates with TanStack Query reduce perceived latency by 60%
- π i18n support - Full English/Spanish localization with automatic detection
- π± PWA capabilities - Offline-first admin panel with service worker caching
- π¨ Accessible UI - WCAG 2.1 AA compliant with Radix UI primitives
- π§ͺ Type-safe - End-to-end TypeScript with 100% coverage in shared packages
Technical Architecture
Monorepo Structure
novahair/
βββ apps/
β βββ admin/ # PWA admin panel (Port 3000)
β βββ booking/ # Booking widget (Port 3001)
β βββ landings/ # Landing templates (Port 3002)
βββ packages/
βββ ui/ # Shared component library (50+ components)
βββ client/ # Type-safe API client with React Query
βββ utils/ # Shared utilities and hooks
βββ book-app/ # Reusable booking logic
Why this architecture?
- Centralized dependencies: Single source of truth for versions
- Instant hot reload: Changes in shared packages reflect immediately
- Type safety: TypeScript works seamlessly across package boundaries
- Optimized builds: pnpmβs content-addressable storage reduces install time by 2x
Live Demo

Core Features
Admin Panel (PWA)
- π Real-time dashboard - Revenue, appointments, and staff performance metrics
- π₯ Team management - Staff scheduling with conflict detection
- π Appointment system - Drag-and-drop calendar with recurring bookings
- πΌ Service catalog - Dynamic pricing and duration management
- π Offline support - Service worker caching with background sync
- π Analytics - Custom date ranges with trend visualization
Booking Widget
- π¨ Embeddable - Works as iframe or React component
- π Multi-step flow - Service β Staff β Time β Confirmation
- π± Responsive - Mobile-first design with touch gestures
- π Multi-tenant - Single deployment serves multiple salons
- πΎ Anonymous sessions - Local storage for guest bookings
- β‘ Optimistic UI - Instant feedback with rollback on error
Landing Pages
- π Customizable - Template system for brand consistency
- π Performance - GSAP animations with 60fps guarantee
- πΈ Parallax effects - Smooth scrolling with Lenis
- π― SEO optimized - SSR with meta tags and sitemap
- π Deep linking - Pre-fill booking from URL params
Technical Deep Dive
Architecture decisions and performance optimizations
Tech Stack
| Layer | Technology | Purpose |
|---|---|---|
| Framework | React 19 | Latest features (use, compiler optimizations) |
| Routing | TanStack Router | Type-safe routing with SSR support |
| State | TanStack Query v5 | Server state with optimistic updates |
| Build | Vite 7 | Lightning-fast HMR (<50ms) |
| Monorepo | pnpm workspaces | Efficient dependency management |
| Styling | Tailwind CSS 4 | Utility-first with JIT compilation |
| UI Primitives | Radix UI | Accessible, unstyled components |
| Animations | GSAP + Framer Motion | High-performance animations |
| i18n | i18next | Runtime translation switching |
| Testing | Vitest + Testing Library | Fast unit and integration tests |
| Linting | Biome | 100x faster than ESLint |
| Type Safety | TypeScript 5.7 | Strict mode with no implicit any |
| SSR | Nitro | Universal server rendering |
Performance Optimizations
1. Code Splitting Strategy
// Route-based splitting with TanStack Router
defaultPreload: "intent" // Preload on hover/focus
defaultViewTransition: true // Native view transitionsResult: Initial bundle reduced from 450KB to 180KB (-60%)
2. PWA Caching Strategy
// Service worker configuration
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.gerardmartinez\.es\/api\/.*/i,
handler: "NetworkFirst",
options: {
cacheName: "api-cache",
expiration: { maxAgeSeconds: 86400 }, // 24h
networkTimeoutSeconds: 10
}
}
]Result: 90% cache hit rate for API calls, offline functionality
3. React Query Optimizations
// Optimistic updates for instant UI feedback
const { create } = useAppointmentActions(tenantId);
create.mutate(data, {
onMutate: async (newAppointment) => {
await queryClient.cancelQueries(['appointments']);
const previous = queryClient.getQueryData(['appointments']);
queryClient.setQueryData(['appointments'], old => [...old, newAppointment]);
return { previous };
},
onError: (err, variables, context) => {
queryClient.setQueryData(['appointments'], context.previous);
}
});Result: Perceived latency reduced by 60%
4. Image Optimization
- WebP format with fallbacks
- Lazy loading with Intersection Observer
- Responsive images with
srcset
Result: LCP improved from 2.1s to 0.8s
Clean Architecture Implementation
Each feature follows a layered architecture:
features/dashboard/
βββ domain/ # Business logic (pure functions)
β βββ metrics.ts
βββ infra/ # External dependencies
β βββ metrics-calculator.ts
β βββ date-range-calculator.ts
βββ hooks/ # React integration
β βββ use-dashboard-metrics.ts
βββ ui/ # Presentation components
βββ dashboard-view.tsx
βββ metric-card.tsxBenefits:
- β Testable business logic (100% coverage in domain layer)
- β Easy to swap implementations (e.g., API client)
- β Clear separation of concerns
- β Reusable across applications
Type-Safe API Client
// Unified client with full type inference
import { client } from '@novahair/client';
const { useAppointments, useAppointmentActions } = client.appointments;
// Fully typed, autocomplete works everywhere
const { appointments, isLoading } = useAppointments(tenantId);
const { create, update, delete: deleteAppointment } = useAppointmentActions(tenantId);
// TypeScript knows the exact shape of appointment
create.mutate({
serviceId: string,
staffId: string,
customer: { name, email, phone },
startsAt: Date,
notes?: string
});Features:
- Zero runtime overhead (types stripped in build)
- Automatic refetch on mutations
- Optimistic updates with rollback
- Anonymous session support for guest bookings
Monorepo Benefits
Shared UI Package (@novahair/ui)
- 50+ components (Button, Input, Dialog, Table, etc.)
- Consistent design tokens
- Radix UI primitives for accessibility
- Tree-shakeable exports
Shared Client Package (@novahair/client)
- Type-safe API client
- React Query integration
- Automatic caching and invalidation
- 100% test coverage
Shared Utils Package (@novahair/utils)
- Custom hooks (useMobile, useDebounce, etc.)
- i18n configuration
- Common utilities (cn, formatters, validators)
Impact:
- 40% reduction in code duplication
- Single source of truth for business logic
- Instant propagation of changes across apps
- Unified testing strategy
Internationalization
// Automatic language detection with cookie persistence
i18n.use(LanguageDetector).init({
supportedLngs: ['en', 'es'],
fallbackLng: 'es',
detection: {
order: ['cookie'],
lookupCookie: 'novahair_i18n',
caches: ['cookie'],
cookieMinutes: 525600 // 1 year
}
});
// Type-safe translations
import { t } from 'i18next';
<h1>{t('welcome_message')}</h1> // TypeScript knows all keysFeatures:
- Runtime language switching (no reload)
- Namespace merging (common + app-specific)
- Pluralization and interpolation
- CLI for extracting missing keys
Testing Strategy
# Unit tests for business logic
pnpm test # Run all tests
pnpm coverage # Generate coverage report
# Integration tests for features
pnpm test --watch # Watch mode for TDDCoverage:
- Domain layer: 100%
- Infrastructure layer: 85%
- UI components: 70%
- Overall: 82%
Tools:
- Vitest (20x faster than Jest)
- Testing Library (user-centric tests)
- jsdom (lightweight DOM simulation)
Build & Deployment
# Development
pnpm dev # All apps in parallel
pnpm dev:admin # Single app
# Production
pnpm build # Optimized builds
# Output: dist/ (SSR-ready with Nitro)Build Metrics:
- Admin: 220KB gzipped (initial bundle)
- Booking: 180KB gzipped
- Landing: 150KB gzipped
- Build time: ~12s (cold), ~2s (cached)
Deployment:
- Vercel (recommended) - Zero config
- Nitro server for SSR
- Automatic preview deployments
- Edge functions for API routes
Local Setup
# Clone repository
git clone https://github.com/polgubau/novahair
cd novahair
# Install dependencies (pnpm required)
pnpm install
# Start all apps
pnpm dev
# Or start individual apps
pnpm dev:admin # http://localhost:3000
pnpm dev:booking # http://localhost:3001
pnpm dev:landing # http://localhost:3002Requirements:
- Node.js β₯ 20.0.0
- pnpm β₯ 9.0.0

Key Learnings
1. Monorepo Management
Managing a monorepo with multiple apps taught me the importance of:
- Dependency boundaries: Preventing circular dependencies between packages
- Build orchestration: Ensuring packages build in the correct order
- Version alignment: Keeping shared dependencies in sync
2. Performance at Scale
Optimizing for performance across three applications required:
- Strategic code splitting: Route-based + component-based splitting
- Caching strategies: Multi-layer caching (browser, service worker, React Query)
- Bundle analysis: Regular audits to prevent bloat
3. Type Safety
Achieving end-to-end type safety involved:
- Shared types: Single source of truth for domain models
- Generic utilities: Reusable type-safe hooks and functions
- Strict TypeScript: No escape hatches, full type coverage
4. Developer Experience
Creating a great DX required:
- Fast feedback loops: HMR in <50ms, tests in <2s
- Clear documentation: README files for each package
- Automated tooling: Biome for instant linting/formatting
Why This Project Matters
NovaHair demonstrates my ability to:
- ποΈ Architect complex systems - Multi-app monorepo with shared packages
- β‘ Optimize performance - Sub-second load times with strategic caching
- π― Write maintainable code - Clean Architecture with 82% test coverage
- π§ Choose the right tools - Modern stack with proven technologies
- π Document thoroughly - Clear README files and inline documentation
- π Ship production code - Deployed and running in production
License
This project is a commercial product and is not open source. All rights reserved by the author.
Contact
Built by:
- Design & Frontend Development: Pol Gubau Amores - polgubau.com
- Database and Backend Development: Gerard MartΓnez Alcocer
Interested in discussing this project or potential opportunities? Feel free to reach out!