Features
UI Components
Tailwind CSS and shadcn/ui components for building interfaces.
Component Library
BetterStarter ships with a comprehensive set of UI components from shadcn/ui, a collection of beautifully designed, accessible components built on Radix UI and styled with Tailwind CSS.
Component Locations
All UI components are located in src/components/:
src/components/
ui/ # shadcn/ui primitives (don't edit manually)
_common/ # Shared components (Header, Footer, etc.)
blog/ # Blog-specific components
dashboard/ # Dashboard componentsUI Primitives
src/components/ui/ contains reusable Radix UI based primitives:
// Button, Input, Dialog, Sheet, etc.
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Dialog } from '@/components/ui/dialog'Tailwind CSS v4
All components use Tailwind CSS v4 with the @tailwindcss/vite plugin.
Utility Classes
<div className="flex items-center gap-4 p-6 rounded-lg bg-primary text-white">
Content
</div>Dark Mode
Dark mode is automatically available through the ThemeProvider:
import { useTheme } from '@/providers/ThemeProvider'
function MyComponent() {
const { theme, setTheme } = useTheme()
return (
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
Toggle theme
</button>
)
}CSS variables for theme switching:
/* src/styles/app.css */
@layer base {
:root {
--primary: #000;
--secondary: #666;
}
[data-theme='dark'] {
--primary: #fff;
--secondary: #999;
}
}Creating Components
Follow TanStack Start conventions:
// src/components/MyComponent.tsx
interface Props {
title: string
onClose?: () => void
}
export function MyComponent({ title, onClose }: Props) {
return (
<div className="bg-card rounded-lg p-6 shadow-sm">
<h2 className="font-semibold text-lg">{title}</h2>
</div>
)
}Common Patterns
Form with shadcn/ui
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
const schema = z.object({
email: z.string().email(),
name: z.string().min(1),
})
export function MyForm() {
const { register, handleSubmit } = useForm({
resolver: zodResolver(schema),
})
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Input {...register('name')} placeholder="Name" />
<Input {...register('email')} placeholder="Email" />
<Button type="submit">Submit</Button>
</form>
)
}Styling Strategy
- Utility-first: Use Tailwind classes for styling
- Component variants: Use CVA (class-variance-authority) for polymorphic components
- Semantic HTML: Use proper HTML elements for accessibility
- Dark mode: Always support light and dark themes
Responsive Design
Use Tailwind's responsive prefixes:
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- Mobile: 1 column, Tablet: 2 columns, Desktop: 4 columns -->
</div>Breakpoints:
sm: 640pxmd: 768pxlg: 1024pxxl: 1280px2xl: 1536px