
Why We Used App Router Server Components for a Multi-Tenant Salon Platform
Because multi-tenant booking systems fail when authorization and rendering drift apart. App Router server components let us resolve role, salon context, and data before client hydration. In this project, middleware, server layouts, and route groups enforce consistent behavior across owner, staff, customer, and admin journeys.

Route-group structure as product architecture
(public): marketplace and booking discovery.(auth): login/signup/business onboarding entry.(protected): owner/staff dashboard operations.portal: customer account experience.admin: super-admin controls.
Server-first data orchestration
const [salonsResult, servicesResult, homepageSettingsResult] =
await Promise.all([
getFeaturedSalons(),
getAllPublicServices(HOMEPAGE_LIMIT),
(async () => {
const supabase = await createClient();
return await supabase
.from("homepage_settings")
.select("section_order, section_heading_overrides, section_visibility")
.eq("id", 1)
.maybeSingle();
})(),
]);
const [salonsResult, servicesResult, productsResult, professionalsResult, packagesResult] =
await Promise.all([
getAllActiveSalons(),
getAllPublicServices(),
getAllPublicProducts(),
getAllPublicProfessionals(),
getAllPublicPackages(),
]);
Middleware as the global contract
The middleware checks role, route intent, and subscription status before protected rendering. That prevents fragmented auth checks across dozens of pages and keeps tenant boundaries consistent.
Practical lesson
In SaaS operations software, server components are not only a performance choice. They are also a governance choice: put business-critical access decisions into central server and middleware layers, then let client components focus on interaction quality.
Read the full implementation context in the case study: /case-studies/cooard-salon-platform