BetterStarter logoBetterStarter
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 components

UI 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: 640px
  • md: 768px
  • lg: 1024px
  • xl: 1280px
  • 2xl: 1536px

On this page