Skip to content

Commit 93bb989

Browse files
authored
feat: Extract UI components into shared package (#2097)
1 parent dbf469d commit 93bb989

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+746
-127
lines changed

CLAUDE.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Full-stack React application template optimized for Cloudflare Workers deploymen
88
- `apps/api/` - tRPC API server
99
- `apps/edge/` - Cloudflare Workers edge deployment
1010
- `packages/core/` - Shared core utilities and WebSocket functionality
11+
- `packages/ui/` - Shared UI components and ShadCN management scripts
1112
- `db/` - Drizzle ORM schemas and migrations
1213
- `infra/` - Terraform infrastructure configuration
1314
- `docs/` - VitePress documentation site
@@ -43,6 +44,12 @@ bun test:web # Test web app (shortcut)
4344
bun test:api # Test API (shortcut)
4445
bun test:edge # Test edge (shortcut)
4546

47+
# UI Components
48+
bun ui:add <component> # Add ShadCN component
49+
bun ui:list # List installed components
50+
bun ui:update # Update all components
51+
bun ui:essentials # Install essential components
52+
4653
# Other
4754
bun lint # Lint all code
4855
bun --filter @repo/db push # Apply DB schema changes

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ This starter kit uses a thoughtfully organized monorepo structure that promotes
3838
- [`docs/`](./docs) — VitePress documentation site
3939
- [`infra/`](./infra) — Terraform infrastructure configurations for multi-environment deployment
4040
- [`scripts/`](./scripts) — Build automation and development tools
41-
- [`apps/web/scripts/`](./apps/web/scripts)ShadCN UI component management utilities
41+
- [`packages/ui/`](./packages/ui)Shared UI components with ShadCN management utilities
4242

4343
**Why Monorepo?** This structure enables seamless code sharing between frontend and backend, ensures type consistency across your entire stack, and simplifies dependency management. When you update a type definition, both client and server stay in sync automatically.
4444

apps/web/README.md

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ app/
3838
├── styles/ # Global styles and theme configuration
3939
├── index.html # HTML template
4040
├── index.tsx # Main application entry point
41-
├── tailwind.config.js # Tailwind CSS configuration
41+
├── tailwind.config.css # Tailwind CSS v4 configuration
4242
└── vite.config.ts # Vite configuration
4343
```
4444

@@ -92,7 +92,7 @@ export const Route = createFileRoute("/dashboard/$orgId")({
9292

9393
## Component Architecture
9494

95-
### UI Components (`components/ui/`)
95+
### UI Components (`@repo/ui`)
9696

9797
ShadCN UI components that you can copy, paste, and own:
9898

@@ -267,26 +267,6 @@ We use CSS variables and Tailwind utilities for consistent theming:
267267
}
268268
```
269269

270-
### Component Styling
271-
272-
Components use Tailwind classes with the `cn()` utility for conditional styling:
273-
274-
```typescript
275-
import { cn } from '@/lib/utils';
276-
277-
function Card({ className, ...props }: CardProps) {
278-
return (
279-
<div
280-
className={cn(
281-
'rounded-lg border bg-card text-card-foreground shadow-sm',
282-
className
283-
)}
284-
{...props}
285-
/>
286-
);
287-
}
288-
```
289-
290270
## Authentication Flow
291271

292272
### Better Auth Integration
@@ -414,14 +394,6 @@ VITE_APP_NAME="React Starter Kit"
414394

415395
## Best Practices
416396

417-
### Component Design
418-
419-
1. **Keep components focused** on single responsibilities
420-
2. **Use TypeScript** for prop validation and documentation
421-
3. **Implement proper error boundaries** for graceful failures
422-
4. **Follow accessibility guidelines** with proper ARIA attributes
423-
5. **Test user interactions** not implementation details
424-
425397
### State Management
426398

427399
1. **Keep atoms small** and focused
@@ -433,8 +405,8 @@ VITE_APP_NAME="React Starter Kit"
433405

434406
1. **Lazy load routes** and heavy components
435407
2. **Optimize images** with proper sizing and formats
436-
3. **Use React.memo** judiciously for expensive components
437-
4. **Implement virtualization** for long lists
408+
3. **Implement code splitting** for better load times
409+
4. **Use React DevTools Profiler** to identify unnecessary renders
438410

439411
## Package Exports
440412

@@ -445,7 +417,7 @@ Clean imports thanks to our package.json exports:
445417
import { cn, formatDate } from "@/lib/utils";
446418

447419
// Import components
448-
import { Button, Card } from "@/components/ui";
420+
import { Button, Card } from "@repo/ui";
449421

450422
// Import hooks
451423
import { useAuth, useLocalStorage } from "@/hooks";

apps/web/components.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"rsc": false,
55
"tsx": true,
66
"tailwind": {
7-
"config": "tailwind.config.js",
87
"css": "styles/globals.css",
98
"baseColor": "neutral",
109
"cssVariables": true,
@@ -13,7 +12,7 @@
1312
"aliases": {
1413
"components": "@/components",
1514
"utils": "@/lib/utils",
16-
"ui": "@/components/ui",
15+
"ui": "@repo/ui",
1716
"lib": "@/lib",
1817
"hooks": "@/hooks"
1918
},

apps/web/components/layout.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
/* SPDX-FileCopyrightText: 2014-present Kriasoft */
22
/* SPDX-License-Identifier: MIT */
33

4+
import { Button, Separator } from "@repo/ui";
45
import { Link } from "@tanstack/react-router";
5-
import { Button } from "@/components/ui/button";
6-
import { Separator } from "@/components/ui/separator";
76

87
export function Layout({ children }: { children: React.ReactNode }) {
98
return (

apps/web/lib/auth.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
/* SPDX-FileCopyrightText: 2014-present Kriasoft */
22
/* SPDX-License-Identifier: MIT */
33

4-
import { createAuthClient } from "better-auth/react";
54
import {
65
anonymousClient,
76
organizationClient,
87
} from "better-auth/client/plugins";
8+
import { createAuthClient } from "better-auth/react";
9+
10+
// Explicitly type the auth client to avoid TypeScript inference issues
11+
// in composite builds
12+
type AuthClient = ReturnType<
13+
typeof createAuthClient<{
14+
plugins: [
15+
ReturnType<typeof anonymousClient>,
16+
ReturnType<typeof organizationClient>,
17+
];
18+
}>
19+
>;
920

10-
export const auth = createAuthClient({
21+
export const auth: AuthClient = createAuthClient({
1122
plugins: [anonymousClient(), organizationClient()],
1223
});

apps/web/lib/utils.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { type ClassValue, clsx } from "clsx";
2-
import { twMerge } from "tailwind-merge";
1+
/* SPDX-FileCopyrightText: 2014-present Kriasoft */
2+
/* SPDX-License-Identifier: MIT */
33

4-
export function cn(...inputs: ClassValue[]) {
5-
return twMerge(clsx(inputs));
6-
}
4+
export { cn } from "@repo/ui";

apps/web/package.json

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@
88
"build": "vite build",
99
"preview": "vite preview",
1010
"test": "vitest",
11-
"coverage": "vitest --coverage",
12-
"ui:list": "bun scripts/ui-list.ts",
13-
"ui:update": "bun scripts/ui-update.ts",
14-
"ui:add": "bun scripts/ui-add.ts",
15-
"ui:essentials": "bun scripts/ui-essentials.ts"
11+
"coverage": "vitest --coverage"
1612
},
1713
"dependencies": {
1814
"@radix-ui/react-checkbox": "^1.3.2",
@@ -22,6 +18,7 @@
2218
"@radix-ui/react-separator": "^1.1.7",
2319
"@radix-ui/react-slot": "^1.2.3",
2420
"@radix-ui/react-switch": "^1.2.5",
21+
"@repo/ui": "*",
2522
"@tanstack/react-router": "^1.130.12",
2623
"better-auth": "^1.3.4",
2724
"class-variance-authority": "^0.7.1",

apps/web/routes/about.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
/* SPDX-FileCopyrightText: 2014-present Kriasoft */
22
/* SPDX-License-Identifier: MIT */
33

4-
import { Button } from "@/components/ui/button";
54
import {
5+
Button,
66
Card,
77
CardContent,
88
CardDescription,
99
CardHeader,
1010
CardTitle,
11-
} from "@/components/ui/card";
12-
import { Separator } from "@/components/ui/separator";
11+
Separator,
12+
} from "@repo/ui";
1313
import { createFileRoute } from "@tanstack/react-router";
1414

1515
export const Route = createFileRoute("/about")({

apps/web/routes/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
/* SPDX-FileCopyrightText: 2014-present Kriasoft */
22
/* SPDX-License-Identifier: MIT */
33

4-
import { Button } from "@/components/ui/button";
54
import {
5+
Button,
66
Card,
77
CardContent,
88
CardDescription,
99
CardHeader,
1010
CardTitle,
11-
} from "@/components/ui/card";
11+
} from "@repo/ui";
1212
import { createFileRoute } from "@tanstack/react-router";
1313

1414
export const Route = createFileRoute("/")({

0 commit comments

Comments
 (0)