Skip to content

shadcn/ui Bulk Installation - Risk Analysis

Risk Level: 🟢 LOW to 🟡 MODERATE

Installing all shadcn/ui components is SAFE for your monorepo with proper precautions. Here’s why:

React versions are locked via resolutions (18.3.1) ✅ shadcn/ui only adds to packages/ui-web (isolated package) ✅ Your apps import from workspace packages (won’t be affected directly) ✅ Radix UI is web-only (no React Native conflicts) ✅ All dependencies use caret ranges (compatible with your existing versions)


1. Dependency Conflicts 🟡 MODERATE RISK

Section titled “1. Dependency Conflicts 🟡 MODERATE RISK”

A. React Version Conflicts

// Your current setup (GOOD):
"resolutions": {
"react": "18.3.1", // ✅ Locked
"react-dom": "18.3.1" // ✅ Locked
}
// shadcn/ui components require:
"peerDependencies": {
"react": "^18.0.0", // ✅ Compatible (18.3.1 satisfies ^18.0.0)
"react-dom": "^18.0.0" // ✅ Compatible
}

Status:NO CONFLICT - Your locked versions satisfy all requirements

B. Radix UI Version Conflicts

// You currently have:
"@radix-ui/react-navigation-menu": "^1.2.14" // From NavigationMenu
// Bulk install will add ~40 more Radix packages:
"@radix-ui/react-dialog": "^1.1.4"
"@radix-ui/react-dropdown-menu": "^2.1.4"
// ... etc

Potential Issue: Some Radix packages may have peer dependency on different @radix-ui/react-* versions Mitigation: Yarn 4 handles this automatically with hoisting

Status: 🟡 MINOR RISK - Yarn will resolve, but watch for warnings

C. Tailwind CSS Conflicts

// Your current setup:
"tailwindcss": "3.4.10" // At root
// shadcn/ui requires:
"tailwindcss": ">=3.0.0" // ✅ Compatible

Status:NO CONFLICT

D. Class Utility Conflicts

// You currently have:
"clsx": "^2.1.1", // ✅ At root AND packages/ui-web
"tailwind-merge": "^3.3.1" // ✅ At root AND packages/ui-web
// shadcn/ui will want to add these to ui-web:
"clsx": "^2.1.1", // ✅ Same version
"tailwind-merge": "^3.3.1" // ✅ Same version

Status:NO CONFLICT - Already installed with correct versions


2. React Native Compatibility 🟢 LOW RISK

Section titled “2. React Native Compatibility 🟢 LOW RISK”

Your monorepo has React Native apps (mobile folders) and web packages (ui-web, ui-web).

Good News:

  • shadcn/ui components live in packages/ui-web/ (web-only package)
  • React Native apps don’t import from @cloudalt-frontend/ui-web
  • React Native apps have their own UI packages: ui-[brand-name]
packages/ui-web/ ← shadcn/ui goes here (WEB ONLY)
└── NavigationMenu
└── Button
└── Card
└── etc
packages/ui-homestay/ ← React Native components
packages/ui-stay-match/ ← React Native components
packages/ui-bonjour-services/ ← React Native components

Potential Issue: If someone accidentally tries to import shadcn component into React Native app

Mitigation:

  1. Keep clear naming: @cloudalt-frontend/ui-web vs @cloudalt-frontend/ui-homestay
  2. Add comment in ui-web package.json:
    {
    "name": "@cloudalt-frontend/ui-web",
    "description": "⚠️ WEB ONLY - Do not import into React Native apps",
    }
  3. TypeScript will catch incompatible imports (Radix uses DOM APIs)

Status: 🟢 LOW RISK - Clear package boundaries prevent issues


packages/ui-web/
└── dependencies: 4 packages (~50KB)
- @radix-ui/react-navigation-menu
- class-variance-authority
- clsx
- tailwind-merge
packages/ui-web/
└── dependencies: ~35 packages (~500KB gzipped)
- All 40+ @radix-ui/react-* packages
- date-fns (for Calendar)
- react-day-picker (for Calendar)
- embla-carousel-react (for Carousel)
- recharts (for Chart components)
- sonner (for Toast)
- vaul (for Drawer)
- react-hook-form (for Form)

Potential Issues:

  1. Larger node_modules (disk space)
  2. Longer yarn install time
  3. Heavier package if all exported

Mitigation:

  • Tree-shaking enabled (Vite + ES modules)
  • Only imported components get bundled
  • Apps won’t bloat unless they import
  • You can remove unused components later

Example Bundle Impact:

// App imports ONLY Button and Card:
import { Button, Card } from '@cloudalt-frontend/ui-web';
// Bundle includes:
// - Button (~5KB)
// - Card (~3KB)
// - Shared utils (~10KB)
// Total: ~18KB (NOT 500KB!)

Status: 🟡 MODERATE RISK - Manageable with tree-shaking


A. Type Definition Conflicts

// Multiple @radix-ui packages exporting similar types
import type { ComponentPropsWithoutRef } from 'react';

Mitigation:

  • Radix UI packages are well-typed
  • No known conflicts between packages
  • Your typescript: "~5.7.2" is up-to-date

Status: 🟢 LOW RISK

B. Build Time Impact

Before: ~10 seconds to build packages/ui-web
After: ~15-20 seconds (50+ components)

Status: 🟢 LOW RISK - Acceptable increase


5. Storybook Performance 🟡 MODERATE RISK

Section titled “5. Storybook Performance 🟡 MODERATE RISK”
Stories: ~20-30 components
Load Time: ~3-5 seconds
Stories: ~80-100 components (existing + shadcn)
Load Time: ~8-12 seconds (estimated)

Potential Issues:

  1. Slower Storybook startup
  2. More memory usage during development
  3. Larger Storybook build

Mitigation:

  • Use Storybook’s lazy loading (already enabled)
  • Split stories by category
  • Use --no-manager-cache flag if needed
  • Consider separate Storybook instances per category

Status: 🟡 MODERATE RISK - May need optimization


6. Workspace Dependency Graph 🟢 LOW RISK

Section titled “6. Workspace Dependency Graph 🟢 LOW RISK”
packages/ui-web → react, react-dom (peers)
apps/[app]/web → @cloudalt-frontend/ui-web
packages/ui-web → react, react-dom, 35+ radix packages
apps/[app]/web → @cloudalt-frontend/ui-web (unchanged)

Key Point: Apps don’t care about ui-web’s internal dependencies!

Status: 🟢 LOW RISK - Encapsulation works correctly


A. Existing NavigationMenu

  • Won’t break - CLI won’t overwrite without --overwrite flag
  • ✅ Your current NavigationMenu stays intact

B. Existing Button (if you have custom one)

packages/ui-web/src/components/ui/button.tsx
// Your existing Button in packages/ui-web/src/components/Button
// Solution: Different paths = no conflict!

C. CSS Variable Conflicts

/* You already have these (from NavigationMenu): */
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
/* etc */
}
/* CLI won't override these if they exist */

Status: 🟢 LOW RISK - CLI respects existing files


Scenario 1: Dependency Version Bump Forces Breaking Change

Section titled “Scenario 1: Dependency Version Bump Forces Breaking Change”

Example:

// Radix releases breaking change:
"@radix-ui/react-dialog": "^2.0.0" // Major version bump
// Your NavigationMenu uses:
"@radix-ui/react-navigation-menu": "^1.2.14" // Depends on Radix core

Prevention:

  1. Pin exact versions instead of caret ranges:
    "@radix-ui/react-dialog": "1.1.4" // Exact, not ^1.1.4
  2. Use resolutions to force versions:
    "resolutions": {
    "@radix-ui/react-*": "~1.x.x"
    }
  3. Test before upgrading

Likelihood: 🟡 MODERATE (Radix is stable but evolves)


Example:

// Your existing component:
<button className="btn btn-primary">Custom</button>
// shadcn Button component:
<Button className="inline-flex items-center ...">shadcn</Button>
// Collision? NO - different classes!

Prevention:

  1. ✅ shadcn uses utility-first Tailwind (no custom classes)
  2. cn() utility merges classes safely
  3. ✅ Your custom classes won’t conflict

Likelihood: 🟢 LOW (different styling approaches)


Example:

// You might have:
import { Button } from '@cloudalt-frontend/ui-web';
// After bulk install:
import { Button } from '@cloudalt-frontend/ui-web'; // Which Button?!

Prevention:

  1. Organize by category:
    // Old custom button:
    export * from './components/custom/Button';
    // New shadcn button:
    export * from './components/forms/button';
    export { Button as ShadcnButton } from './components/forms/button';
  2. Use explicit exports:
    packages/ui-web/src/index.ts
    export { Button as CustomButton } from './custom/Button';
    export { Button as ShadcnButton } from './components/ui/button';

Likelihood: 🟡 MODERATE (if you have existing components with same names)


Example:

Terminal window
yarn install
# Error: Could not resolve peer dependency @radix-ui/react-compose-refs

Prevention:

  1. Use --ignore-scripts if needed:
    Terminal window
    yarn install --ignore-scripts
  2. Clear cache if stuck:
    Terminal window
    yarn cache clean
    rm -rf node_modules
    yarn install
  3. Use Yarn 4’s nodeLinker: node-modules (you already have this)

Likelihood: 🟢 LOW (Yarn 4 is robust)


Terminal window
# 1. Create safety checkpoint
cd /Users/work-station/company/cloudalt-frontend
git checkout -b test/shadcn-bulk-install
git add .
git commit -m "checkpoint: before shadcn bulk install"
# 2. Install to ui-web package
cd packages/ui-web
npx shadcn@latest add button card input alert # Start with 4 components
# 3. Check for issues
cd ../..
yarn install
yarn build # Build all packages

Success Criteria:

  • ✅ No yarn install errors
  • ✅ No TypeScript errors
  • ✅ Storybook still loads
  • ✅ Existing components still work

Terminal window
# Install by category (safer than --all at once)
# Round 1: Core UI (10 components)
cd packages/ui-web
npx shadcn@latest add button input textarea select checkbox radio-group switch label badge avatar
# Test
cd ../..
yarn storybook # Verify in Storybook
# Round 2: Layout (10 components)
cd packages/ui-web
npx shadcn@latest add card separator sheet drawer tabs accordion collapsible dialog alert-dialog
# Round 3: Feedback (10 components)
npx shadcn@latest add toast tooltip popover hover-card progress skeleton alert
# Round 4: Navigation (5 components)
npx shadcn@latest add breadcrumb menubar dropdown-menu context-menu pagination
# Round 5: Data & Advanced (15+ components)
npx shadcn@latest add table calendar chart carousel date-picker slider

Benefit: Catch issues early, easier to rollback


Terminal window
# 1. Run all checks
yarn nx run-many --target=build --all
yarn nx run-many --target=lint --all
yarn nx run-many --target=test --all
# 2. Build Storybook
yarn build-storybook
# 3. Test in one app
cd apps/bonjour_services/bonjour_it_com/web
# Import and test shadcn components

Option 1: Git Revert (Safest)

Terminal window
# If you committed checkpoint:
git reset --hard HEAD~1
# If you want to keep other changes:
git checkout HEAD~1 -- packages/ui-web/

Option 2: Selective Removal

Terminal window
# Remove problematic component
cd packages/ui-web
rm -rf src/components/ui/[component-name]
# Remove from package.json
# Edit manually or:
yarn remove @radix-ui/react-[component-name]

Option 3: Fresh Start

Terminal window
# Delete ui-web and recreate
cd packages
rm -rf ui-web
mkdir ui-web
# Recreate from scratch

Terminal window
# Build and analyze
yarn nx build ui-web
yarn analyze-bundle
# Expected increases:
# - node_modules: +50-100MB
# - Bundle (if all components used): +500KB gzipped
# - Bundle (typical app): +50-100KB gzipped
Terminal window
# Before:
time yarn nx build ui-web
# Expected: ~10-15 seconds
# After:
time yarn nx build ui-web
# Expected: ~15-25 seconds
Terminal window
yarn install 2>&1 | grep "warning"
# Look for:
# - Unmet peer dependencies
# - Conflicting versions
# - Deprecated packages

Before bulk install:

  • Create git branch test/shadcn-bulk-install
  • Commit current state as checkpoint
  • Document current bundle sizes
  • Take note of current build times
  • List all existing components in ui-web

During install:

  • Install in phases (not all at once)
  • Test after each phase
  • Monitor yarn install output
  • Check for TypeScript errors
  • Verify Storybook loads

After install:

  • Run full build: yarn nx run-many --target=build --all
  • Run all tests: yarn nx run-many --target=test --all
  • Verify Storybook: yarn storybook
  • Test in one app (bonjour_it_com/web)
  • Compare bundle sizes
  • Update documentation

  1. Use gradual rollout (not --all at once)
  2. Test in isolated branch first
  3. Install by category (10-15 components per round)
  4. Validate after each round
  5. Monitor for warnings
  • ✅ React versions locked via resolutions
  • ✅ Package isolation (ui-web is separate)
  • ✅ Tree-shaking prevents bloat
  • ✅ Git allows easy rollback
  • ✅ Yarn 4 handles conflicts well
Risk CategoryLevelMitigation
Dependency Conflicts🟡 ModerateLocked React versions, gradual install
React Native Compat🟢 LowClear package boundaries
Bundle Size🟡 ModerateTree-shaking, selective imports
TypeScript🟢 LowWell-typed packages
Storybook Perf🟡 ModerateLazy loading, category split
Breaking Changes🟢 LowGit rollback, pinned versions

Overall Risk: 🟡 LOW-MODERATE - Safe to proceed with proper testing


  1. Do you want me to create the test branch and do Phase 1 (test install)?
  2. Should we start with just 10 core components to validate?
  3. Do you have any existing components in ui-web that might conflict?
  4. What’s your preferred rollback strategy if issues arise?