Reusable UI components for the React Starter Kit monorepo. Built with React 19, TypeScript, and Tailwind CSS because we believe in using components that actually work.
We're shipping shadcn/ui components built on Radix UI primitives, styled with Tailwind CSS v4, and typed with TypeScript. Components that you can copy, paste, and actually own.
- Copy & Own: No black-box component library dependencies
- Radix UI Primitives: Accessible components that work everywhere
- Tailwind Styled: Utility-first styling that's fast and predictable
- TypeScript First: Full type safety because runtime errors are so 2019
- Monorepo Ready: Share components across all apps without the headache
# Install (happens automatically with bun install in root)
bun install
Components use Tailwind classes with the cn() utility for conditional styling:
import { cn } from "@repo/ui";
function Card({ className, ...props }: CardProps) {
return (
<div
className={cn(
'rounded-lg border bg-card text-card-foreground shadow-sm',
className
)}
{...props}
/>
);
}import { Button, Card, Input, cn } from "@repo/ui";
function MyComponent() {
return (
<Card>
<Input placeholder="Type something..." />
<Button variant="outline">Click me</Button>
</Card>
);
}packages/ui/
├── components/ # shadcn/ui components (the good stuff)
├── hooks/ # Custom React hooks (when we need them)
├── lib/ # Utilities (cn function and friends)
├── scripts/ # shadcn/ui component management utilities
├── index.ts # Barrel exports for clean imports
└── components.json # shadcn/ui configuration magicimport { Button, Card, Input, cn } from "@repo/ui";import { Button } from "@repo/ui/components/button";
import { cn } from "@repo/ui/lib/utils";Modern bundlers handle tree-shaking automatically, but direct imports guarantee minimal bundle size if you're obsessive about performance.
Form Elements: Button, Input, Textarea, Checkbox, Switch, Select, RadioGroup
Layout: Card, Separator, ScrollArea
Feedback: Skeleton
Each component comes with:
- Multiple variants and sizes
- Full TypeScript support
- Tailwind CSS styling
- Radix UI accessibility baked in
// Button with all the variants you need
<Button variant="destructive" size="lg">Delete Everything</Button>
<Button variant="outline" size="sm">Maybe Don't</Button>
// Card with semantic structure
<Card>
<CardHeader>
<CardTitle>Something Important</CardTitle>
<CardDescription>But not too important</CardDescription>
</CardHeader>
<CardContent>
<p>The actual content</p>
</CardContent>
</Card>Consuming apps must include UI package paths in their Tailwind CSS v4 config:
/* apps/web/tailwind.config.css */
@import "tailwindcss";
/* Content paths for Tailwind to scan */
@source "./routes/**/*.{ts,tsx}";
@source "./components/**/*.{ts,tsx}";
/* Include UI components for Tailwind compilation */
@source "../../packages/ui/components/**/*.{ts,tsx}";
@source "../../packages/ui/lib/**/*.{ts,tsx}";
@source "../../packages/ui/hooks/**/*.{ts,tsx}";
/* Custom dark mode variant */
@custom-variant dark (&:is(.dark *));
/* Theme configuration */
@theme inline {
/* Map CSS variables to Tailwind utilities */
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
/* ... continue with other color mappings */
}{
"compilerOptions": {
"paths": {
"@repo/ui": ["../../packages/ui"],
"@repo/ui/*": ["../../packages/ui/*"]
}
}
}- No CSS bundling: This package ships TypeScript, not CSS
- CSS Variables: Components use
hsl(var(--primary))for theming - App responsibility: Consuming apps handle their own global styles and CSS variables
# From project root
bun run ui:add button # Add a single component
bun run ui:add button card # Add multiple components
bun run ui:essentials # Install curated essential components (37 components)
# From packages/ui directory
bun run ui:add dialogbun run ui:list # Show all installed components with metadatabun run ui:update # Update all components to latest versions
bun run ui:update button # Update specific componentThe ui:essentials script installs a curated set of 37 components perfect for most applications:
- Forms: button, input, textarea, select, checkbox, radio-group, switch, label, form
- Layout: card, separator, skeleton, scroll-area
- Navigation: navigation-menu, breadcrumb, tabs
- Feedback: dialog, alert-dialog, toast, alert, badge, progress
- Data Display: avatar, tooltip, popover
bun run ui:essentials --list # Preview components without installing
bun run ui:essentials # Install all essential componentsAll scripts include:
- Automatic Prettier formatting after generation
- Progress indicators and clear success/error messages
- Built-in help with
--helpflag
Components use @/ aliases internally:
// Inside components
import { cn } from "@/lib/utils";
// External apps import
import { Button } from "@repo/ui";This keeps internal imports clean while maintaining external compatibility.
- Keep components focused on single responsibilities
- Use TypeScript for prop validation and documentation
- Implement proper error boundaries for graceful failures
- Follow accessibility guidelines with proper ARIA attributes
- Test user interactions not implementation details
- Use React.memo judiciously for expensive components
- Implement virtualization for long lists
- Optimize re-renders by memoizing callbacks and values