Component Import Patterns Guide
This guide defines the component layering system and import patterns for the CloudAlt monorepo. The system follows a three-tier architecture: Core → Division → App, with clear rules for overrides and promotion workflows.
Component Layers
Section titled “Component Layers”Layer 1: Core (Generic, Shared)
Section titled “Layer 1: Core (Generic, Shared)”Location: packages/ui, packages/ui-web
Import: @cloudalt-frontend/ui, @cloudalt-frontend/ui-web
Depends on: Foundation packages only (theme, icons, common)
Tags: layer:core-ui, scope:shared
Purpose: Cross-app primitives used everywhere—buttons, inputs, typography, layout components, icons.
Examples:
import { Button } from '@cloudalt-frontend/ui-web';import { Typography } from '@cloudalt-frontend/ui';import { Icon } from '@cloudalt-frontend/icons';When to use: Components with zero brand-specific logic, used across multiple divisions.
Layer 2: Division (App Family)
Section titled “Layer 2: Division (App Family)”Location: packages/ui-<division>
Import: @cloudalt-frontend/ui-<division>
Depends on: Core packages only
Tags: layer:division-ui, division:<name>
Available Divisions:
ui-stay-match– Stay Match family (Pink Guest, Orange Guest, Rainbow Host)ui-altfinder– AltFinder family (AltFinder, Orange Finder, Pink Finder)ui-roommate– Roommate family (Roommate Guru, Roommate Help, Roommate Works)ui-bonjour-services– Bonjour Services family (Bonjour Locker, Bonjour IT, Lingua IT)ui-greenhost– Greenhostui-homestay– Homestay familyui-guestroom– Guestroom familyui-pride-city– Pride Cityui-room-lgbt– Room LGBT familyui-hostguest– Hostguest family
Purpose: Shared patterns within an app family—hero sections, CTAs, navigation menus, auth flows, booking components.
Examples:
import { Hero } from '@cloudalt-frontend/ui-stay-match';import { BookingCard } from '@cloudalt-frontend/ui-stay-match';import { ProfileHeader } from '@cloudalt-frontend/ui-roommate';When to use: Components shared by 2+ apps within the same division, with division-specific business logic or styling.
Layer 3: App (Rare, App-Specific)
Section titled “Layer 3: App (Rare, App-Specific)”Location: apps/<division>/<app>/<platform>/src/components/
Import: @app/components (local alias)
Depends on: Division and Core packages
Tags: layer:app, division:<name>, platform:web|mobile
Purpose: Last-mile, app-only customizations or branded wrappers.
Examples:
// Inside apps/stay_match/pinkguest/web/src/import { Hero } from '@app/components/Hero'; // App-specific overrideimport { BookingCard } from '@cloudalt-frontend/ui-stay-match'; // Division defaultimport { Button } from '@cloudalt-frontend/ui-web'; // Core fallbackWhen to use: One-off components or variants that don’t (yet) make sense to share, or temporary experimental features.
Import Priority Rules
Section titled “Import Priority Rules”Components should be imported in this order of preference:
- App layer first (if override exists):
@app/components/MyComponent - Division layer second (family-specific):
@cloudalt-frontend/ui-<division> - Core layer last (generic):
@cloudalt-frontend/ui-webor@cloudalt-frontend/ui
Example: Import Hierarchy
Section titled “Example: Import Hierarchy”// ✅ Good: Check app layer first, fallback to division, then coreimport { Hero } from '@app/components'; // App-specific Hero overrideimport { BookingCard } from '@cloudalt-frontend/ui-stay-match'; // Division componentimport { Button } from '@cloudalt-frontend/ui-web'; // Core component
// ❌ Bad: Skipping layers or importing from wrong divisionimport { Hero } from '@cloudalt-frontend/ui-web'; // Wrong—use division or appimport { ProfileCard } from '@cloudalt-frontend/ui-roommate'; // Wrong—cross-division importSetting Up Per-App Aliases
Section titled “Setting Up Per-App Aliases”Each app needs a local @app/components alias in its tsconfig.json:
Web App Example
Section titled “Web App Example”File: apps/stay_match/pinkguest/web/tsconfig.json
{ "extends": "../../../../tsconfig.base.json", "compilerOptions": { "paths": { "@app/components": ["apps/stay_match/pinkguest/web/src/components/index.ts"], "@app/components/*": ["apps/stay_match/pinkguest/web/src/components/*"] } }, "files": [], "include": ["src/**/*"], "references": []}Mobile App Example
Section titled “Mobile App Example”File: apps/stay_match/pinkguest/mobile/tsconfig.json
{ "extends": "../../../../tsconfig.base.json", "compilerOptions": { "paths": { "@app/components": ["apps/stay_match/pinkguest/mobile/src/components/index.ts"], "@app/components/*": ["apps/stay_match/pinkguest/mobile/src/components/*"] } }, "files": [], "include": ["src/**/*"], "references": []}Creating the Components Index
Section titled “Creating the Components Index”Create a barrel export at src/components/index.ts:
export { Hero } from './Hero';export { CustomBookingFlow } from './CustomBookingFlow';// Export other app-specific overrides hereOverride Workflow
Section titled “Override Workflow”Step 1: Identify Need for Override
Section titled “Step 1: Identify Need for Override”You need an app-level override when:
- A division component needs brand-specific tweaks for one app only
- You’re experimenting with a new pattern before committing to division-wide adoption
- The app has unique business logic that doesn’t belong in the division layer
Step 2: Create App Override
Section titled “Step 2: Create App Override”Location: apps/<division>/<app>/<platform>/src/components/MyComponent.tsx
Pattern 1: Wrap and Extend (Recommended)
import { Hero as DivisionHero, HeroProps } from '@cloudalt-frontend/ui-stay-match';
export function Hero(props: HeroProps) { return ( <div className="pinkguest-custom-wrapper"> <DivisionHero {...props} /> {/* Add app-specific content */} <div className="custom-badge">Pink Guest Exclusive</div> </div> );}Pattern 2: Full Replacement (When needed)
import { HeroProps } from '@cloudalt-frontend/ui-stay-match'; // Keep same interface
export function Hero(props: HeroProps) { // Completely custom implementation return ( <section className="custom-hero"> <h1>{props.title}</h1> {/* Custom logic */} </section> );}Step 3: Export from Index
Section titled “Step 3: Export from Index”export { Hero } from './Hero';Step 4: Use in App
Section titled “Step 4: Use in App”import { Hero } from '@app/components'; // Uses your override
function HomePage() { return <Hero title="Welcome to Pink Guest" />;}Promotion Workflow
Section titled “Promotion Workflow”When an app-level component proves useful across the division or monorepo:
Promote: App → Division
Section titled “Promote: App → Division”-
Move the file:
Terminal window cp apps/stay_match/pinkguest/web/src/components/Hero.tsx \packages/ui-stay-match/src/components/Hero/Hero.tsx -
Update division exports:
packages/ui-stay-match/src/index.ts export { Hero } from './components/Hero/Hero';export type { HeroProps } from './components/Hero/Hero'; -
Update app imports:
apps/stay_match/pinkguest/web/src/pages/Home.tsx - import { Hero } from '@app/components';+ import { Hero } from '@cloudalt-frontend/ui-stay-match'; -
Remove app override:
Terminal window rm apps/stay_match/pinkguest/web/src/components/Hero.tsx# Update components/index.ts to remove export
Promote: Division → Core
Section titled “Promote: Division → Core”-
Generalize the component (remove division-specific logic, use tokens for styling)
-
Move to core:
Terminal window cp packages/ui-stay-match/src/components/Hero/Hero.tsx \packages/ui-web/src/components/Hero/Hero.tsx -
Update core exports:
packages/ui-web/src/index.ts export { Hero } from './components/Hero/Hero';export type { HeroProps } from './components/Hero/Hero'; -
Update all apps to import from core:
- import { Hero } from '@cloudalt-frontend/ui-stay-match';+ import { Hero } from '@cloudalt-frontend/ui-web'; -
Remove division version (after all apps updated)
Using Storybook to Preview
Section titled “Using Storybook to Preview”Storybook has a Division and App selector in the toolbar:
- Open Storybook:
yarn nx storybook storybook - Select your division from the toolbar (e.g., “Stay Match”)
- Select your app (e.g., “Pink Guest”)
- Components will render with the correct brand tokens and theme
This lets you preview how division components look in each app context before creating overrides.
Module Boundaries (Enforced by Nx)
Section titled “Module Boundaries (Enforced by Nx)”The layering rules are enforced via Nx tags and depConstraints:
- Apps (
layer:app) → can depend onlayer:division-uiandlayer:core-ui - Division UI (
layer:division-ui) → can depend onlayer:core-uionly - Core UI (
layer:core-ui) → no dependencies on other UI layers - Cross-division imports are blocked (e.g., Stay Match UI can’t import Roommate UI)
To check boundaries:
yarn nx lintQuick Reference
Section titled “Quick Reference”| Layer | Location | Import | Depends On | When to Use |
|---|---|---|---|---|
| App | apps/.../src/components/ | @app/components | Division + Core | Rare, app-only overrides |
| Division | packages/ui-<division>/ | @cloudalt-frontend/ui-<division> | Core only | Shared within app family |
| Core | packages/ui, packages/ui-web | @cloudalt-frontend/ui-web | Foundation | Cross-app primitives |
Common Patterns
Section titled “Common Patterns”Pattern: Branded Wrapper
Section titled “Pattern: Branded Wrapper”import { BookingCard as DivisionCard, BookingCardProps } from '@cloudalt-frontend/ui-stay-match';
export function BookingCard(props: BookingCardProps) { return ( <DivisionCard {...props} className={`${props.className} pinkguest-booking-style`} brandBadge="Pink Guest Verified" /> );}Pattern: Token-First Customization
Section titled “Pattern: Token-First Customization”// Prefer: Use design tokens (no override needed)<Hero title="Welcome" className="bg-primary text-primary-foreground" />
// Instead of: Creating app override just for colorsPattern: Conditional App Logic
Section titled “Pattern: Conditional App Logic”import { Hero as DivisionHero, HeroProps } from '@cloudalt-frontend/ui-stay-match';
export function Hero(props: HeroProps) { const isPinkGuestUser = useAuth().isPinkGuest; // App-specific logic
return ( <DivisionHero {...props}> {isPinkGuestUser && <PinkGuestPromoBar />} </DivisionHero> );}Q: Can I import from a different division’s UI package?
A: No. Cross-division imports are blocked by Nx boundaries. If you need a component from another division, promote it to Core first.
Q: How do I know when to promote a component?
A: If 2+ apps in the same division use it → promote to division. If 2+ divisions use it → promote to core.
Q: Can I have app-specific stories in Storybook?
A: Yes, but keep them opt-in. Most stories should import from division/core packages. Use the toolbar to preview in different app contexts.
Q: What if an override needs to change the component API?
A: Keep the same interface initially (for easy promotion). If the API must change, document it clearly and consider if it should be a new component instead.
Q: How do I handle mobile-specific overrides?
A: Same pattern—use @app/components alias in mobile app tsconfig. Division packages can also export platform-specific variants.
Next Steps
Section titled “Next Steps”- Add
@app/componentsalias to your app’stsconfig.json - Create
src/components/index.tsbarrel export - Try creating a simple override component
- Use Storybook toolbar to preview in different app contexts
- Run
yarn nx lintto validate module boundaries
For generator-based promotion, see: Component Promotion Guide (coming soon)