preferences-react-tanstack-ui-development

React and TanStack UI development patterns including component design, routing, and state management. Load when working with React components or TanStack libraries.

16 stars

Best use case

preferences-react-tanstack-ui-development is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

React and TanStack UI development patterns including component design, routing, and state management. Load when working with React components or TanStack libraries.

Teams using preferences-react-tanstack-ui-development should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/preferences-react-tanstack-ui-development/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/development/preferences-react-tanstack-ui-development/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/preferences-react-tanstack-ui-development/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How preferences-react-tanstack-ui-development Compares

Feature / Agentpreferences-react-tanstack-ui-developmentStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

React and TanStack UI development patterns including component design, routing, and state management. Load when working with React components or TanStack libraries.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# React / TanStack UI development

## Architectural patterns alignment

See @~/.claude/skills/preferences-architectural-patterns/SKILL.md for overarching principles.

Apply functional programming patterns to React UI development:
- Components are pure functions from props to JSX
- State transformations should be explicit and traceable
- Side effects isolated to hooks and boundaries (useEffect, mutations)
- Composition over inheritance for component design
- Type-safety enforced from routing through rendering

## TanStack ecosystem integration

The TanStack ecosystem provides type-safe, composable primitives for modern React applications.

### Core libraries

- **TanStack Router**: File-based routing with full type inference for params, search, and loaders
- **TanStack Query**: Declarative server state management with automatic caching and revalidation
- **TanStack Form**: Type-safe form handling with validation and error management
- **TanStack Store**: Reactive client state with fine-grained subscriptions
- **TanStack DB**: Reactive collections with live queries for complex client-side data

### Integration pattern

```typescript
// TanStack Router + Query + tRPC creates end-to-end type safety
import { createFileRoute } from '@tanstack/react-router'
import { api } from '@/lib/trpc'

export const Route = createFileRoute('/users/$userId')({
  // Loader provides type-safe data to component
  loader: async ({ params }) => {
    const user = await api.users.getById.query(params.userId)
    return { user }
  },
})

function UserProfile() {
  const { user } = Route.useLoaderData() // Fully typed!
  // ...
}
```

## State management philosophy

Separate concerns between server state and client state.

### Server state (TanStack Query)

Use TanStack Query for any data that originates from the server:
- API responses
- Database queries via tRPC
- Remote resources and assets
- Paginated and infinite lists

**Key patterns**:
- Let TanStack Query handle caching, revalidation, and background updates
- Use optimistic updates for perceived performance
- Leverage query invalidation for cache management
- Structure queries with proper keys for selective invalidation

```typescript
// Server state with TanStack Query + tRPC
import { api } from '@/lib/trpc'

function UserList() {
  const { data, isLoading } = api.users.list.useQuery()
  const updateUser = api.users.update.useMutation({
    onSuccess: () => {
      // Invalidate to refetch
      utils.users.list.invalidate()
    }
  })

  // ...
}
```

### Client state (TanStack Store / Zustand)

Use client state management for UI-only state:
- Form field values before submission
- UI toggles (modals, dropdowns, themes)
- Client-side filters and sorts
- Transient interaction state

**Key patterns**:
- Keep client state minimal and derive from server state when possible
- Use TanStack Store for reactive updates with fine-grained subscriptions
- Consider Zustand for simpler, less reactive client state needs
- Avoid duplicating server state in client state

```typescript
// Client state with Zustand
import { create } from 'zustand'

interface UIState {
  isModalOpen: boolean
  selectedIds: Set<string>
  toggleModal: () => void
  selectId: (id: string) => void
}

const useUIStore = create<UIState>((set) => ({
  isModalOpen: false,
  selectedIds: new Set(),
  toggleModal: () => set((state) => ({ isModalOpen: !state.isModalOpen })),
  selectId: (id) => set((state) => ({
    selectedIds: new Set(state.selectedIds).add(id)
  })),
}))
```

## Pragmatic exceptions for specialized use cases

### Client-side analytics with DuckDB WASM

For applications performing client-side data analysis with DuckDB WASM (e.g., using SQLRooms), **Zustand is acceptable** instead of TanStack Store when:
- The application architecture follows vertical slices isolating analytics concerns
- Type-safe data source configuration uses Zod schemas (ADT alignment maintained)
- Query execution and state updates are confined to dedicated slices
- The imperative state patterns do not leak into general application logic

**Rationale**: DuckDB WASM analytics applications require fine-grained control over query execution, caching, and result streaming.
Zustand's imperative API provides ergonomic access to these patterns without the overhead of TanStack Store's reactive primitives.
The vertical slice architecture ensures analytics state remains isolated from general UI state.

#### SQLRooms vertical slice pattern

SQLRooms implements a composable slice architecture where each feature is self-contained:

```typescript
// Vertical slice contains: state + actions + UI + config schema
import { createRoomStore, createRoomShellSlice } from '@sqlrooms/room-shell'
import { createSqlEditorSlice } from '@sqlrooms/sql-editor'
import { createWasmDuckDbConnector } from '@sqlrooms/duckdb'
import { z } from 'zod'
import { persist } from 'zustand/middleware'

// Type-safe configuration with Zod (ADT pattern)
const RoomConfig = BaseRoomConfig.merge(SqlEditorSliceConfig)
type RoomConfig = z.infer<typeof RoomConfig>

// Compose slices into unified store
const { roomStore, useRoomStore } = createRoomStore<RoomConfig, RoomState>(
  persist(
    (set, get, store) => ({
      // DuckDB connector slice
      ...createRoomShellSlice({
        connector: createWasmDuckDbConnector(),
        config: { layout: {...}, dataSources: [...] },
        room: { panels: {...} }
      })(set, get, store),

      // SQL editor slice
      ...createSqlEditorSlice()(set, get, store),

      // Custom application slice
      customAnalytics: {
        filters: {},
        applyFilter: (filter) => set({ customAnalytics: {...} })
      }
    }),
    {
      name: 'analytics-app-storage',
      // Only persist configuration, not runtime state
      partialize: (state) => ({
        config: RoomConfig.parse(state.config)
      })
    }
  )
)
```

#### Type-safe S3/HTTPFS data sources with Zod

Use discriminated unions for data source configuration (matches ADT patterns from @~/.claude/skills/preferences-algebraic-data-types/SKILL.md):

```typescript
// From @sqlrooms/room-config - Zod schemas for type safety
const DataSourceTypes = z.enum(['file', 'url', 'sql'])

const BaseDataSource = z.object({
  type: DataSourceTypes,
  tableName: z.string()
})

// Discriminated union for different source types
const UrlDataSource = BaseDataSource.extend({
  type: z.literal('url'),
  url: z.string(),
  loadOptions: z.object({
    method: z.enum(['read_csv', 'read_parquet']),
    select: z.array(z.string()).optional(),
    where: z.string().optional()
  }).optional(),
  httpMethod: z.string().optional(),
  headers: z.record(z.string(), z.string()).optional()
})

const DataSource = z.discriminatedUnion('type', [
  FileDataSource,
  UrlDataSource,
  SqlQueryDataSource
])

// S3 data source configuration for public ducklake data
const s3DataSource: UrlDataSource = {
  type: 'url',
  tableName: 'public_ducklake_data',
  // DuckDB WASM with httpfs extension supports direct S3 access
  url: 's3://ducklake-public/data/events.parquet',
  loadOptions: {
    method: 'read_parquet',
    // Push-down filters to parquet reader (performance optimization)
    select: ['user_id', 'event_type', 'created_at', 'payload'],
    where: "created_at >= '2024-01-01' AND event_type = 'UserCreated'"
  }
}

// Configuration validated at runtime and compile-time
const config = {
  dataSources: [s3DataSource]
}
RoomConfig.parse(config) // Runtime validation
```

#### Railway-oriented programming for query error handling

Apply Result types to query execution for explicit error handling.

See @~/.claude/skills/preferences-railway-oriented-programming/SKILL.md for Result patterns and error composition.

```typescript
import { z } from 'zod'

// Result type for query operations
type Result<T, E> =
  | { ok: true; value: T }
  | { ok: false; error: E }

const success = <T, E>(value: T): Result<T, E> =>
  ({ ok: true, value })

const failure = <T, E>(error: E): Result<T, E> =>
  ({ ok: false, error })

// Error types as discriminated union
type QueryError =
  | { type: 'connection'; message: string }
  | { type: 'syntax'; message: string; line?: number }
  | { type: 'data_source'; message: string; source: string }
  | { type: 'network'; message: string; status?: number }

// Query execution with Result type
const executeQuery = async (
  sql: string,
  db: DuckDB
): Promise<Result<QueryResult, QueryError>> => {
  try {
    // Validate SQL syntax (applicative validation)
    const syntaxResult = validateSqlSyntax(sql)
    if (!syntaxResult.ok) return syntaxResult

    // Execute query
    const result = await db.query(sql)
    return success(result)
  } catch (error) {
    // Map exceptions to typed errors
    if (error.message.includes('Catalog Error')) {
      return failure({
        type: 'data_source',
        message: 'Table not found or data source not loaded',
        source: extractTableName(error.message)
      })
    }
    if (error.message.includes('Syntax error')) {
      return failure({
        type: 'syntax',
        message: error.message,
        line: extractLineNumber(error.message)
      })
    }
    return failure({
      type: 'connection',
      message: error.message
    })
  }
}

// Zustand slice with railway-oriented query actions
const createQuerySlice = () => (set, get, store) => ({
  query: {
    sql: '',
    result: null as QueryResult | null,
    error: null as QueryError | null,
    isExecuting: false,

    // Action with explicit error handling
    executeQuery: async (sql: string) => {
      set({ query: { ...get().query, isExecuting: true, error: null } })

      const result = await executeQuery(sql, get().db.connection)

      // Railway tracks: success or failure
      if (result.ok) {
        set({
          query: {
            ...get().query,
            result: result.value,
            isExecuting: false
          }
        })
      } else {
        set({
          query: {
            ...get().query,
            error: result.error,
            isExecuting: false
          }
        })
      }
    }
  }
})

// UI component with pattern matching on error type
function QueryErrorDisplay({ error }: { error: QueryError }) {
  switch (error.type) {
    case 'syntax':
      return (
        <div className="text-destructive">
          <p>Syntax error{error.line ? ` at line ${error.line}` : ''}</p>
          <p className="text-sm">{error.message}</p>
        </div>
      )
    case 'data_source':
      return (
        <div className="text-destructive">
          <p>Data source error: {error.source}</p>
          <p className="text-sm">{error.message}</p>
          <button onClick={loadDataSource}>Load data source</button>
        </div>
      )
    case 'network':
      return (
        <div className="text-destructive">
          <p>Network error{error.status ? ` (${error.status})` : ''}</p>
          <p className="text-sm">{error.message}</p>
          <button onClick={retry}>Retry</button>
        </div>
      )
    case 'connection':
      return (
        <div className="text-destructive">
          <p>Connection error</p>
          <p className="text-sm">{error.message}</p>
        </div>
      )
  }
}
```

#### S3/HTTPFS configuration requirements

For browser-based S3 queries with DuckDB WASM httpfs extension:

**CORS configuration**: S3 bucket must allow browser origins

```json
{
  "CORSRules": [
    {
      "AllowedOrigins": ["https://your-app.com"],
      "AllowedMethods": ["GET", "HEAD"],
      "AllowedHeaders": ["*"],
      "ExposeHeaders": ["ETag", "Content-Length"]
    }
  ]
}
```

**DuckDB httpfs extension**: Automatically loaded by @sqlrooms/duckdb

```typescript
// Extension loads transparently when using s3:// URLs
const connector = createWasmDuckDbConnector({
  // Optional: Initialize additional extensions
  initializationQuery: `
    INSTALL httpfs;
    LOAD httpfs;
    SET s3_region='us-east-1';
  `
})
```

**Direct parquet queries**: No table creation needed for one-off queries

```typescript
// Query parquet files directly from S3
const adhocQuery = `
  SELECT
    event_type,
    COUNT(*) as count
  FROM read_parquet('s3://ducklake-public/data/events/*.parquet')
  WHERE created_at >= '2024-01-01'
  GROUP BY event_type
`

const result = await executeQuery(adhocQuery, db)
```

#### When to use this pattern

Use SQLRooms with Zustand for:
- **Client-side analytics applications**: User-facing data exploration tools
- **Privacy-preserving analytics**: Data never leaves the user's device
- **Offline-first data tools**: Full query capabilities without server
- **Local-first dashboards**: Real-time visualization of local/remote data

Return to TanStack Store for:
- **General application state**: Non-analytics UI state
- **Server state management**: Use TanStack Query + tRPC instead
- **Real-time collaboration**: Use TanStack DB for live queries

### TanStack DB for reactive collections

Use TanStack DB when you need multiple live views of the same data:
- Dashboards with filtered sections
- Real-time collaborative interfaces
- Complex client-side aggregations
- Data tables with dynamic filtering

```typescript
// Reactive collections with TanStack DB
import { createCollection } from '@tanstack/react-db'
import { useLiveQuery } from '@tanstack/react-db'

const todoCollection = createCollection(
  queryCollectionOptions<Todo>({
    queryKey: ['todos'],
    queryFn: async () => api.todos.list.query(),
    queryClient,
    getKey: (item) => item.id,
  })
)

// Multiple views of same data, all automatically reactive
function TodoDashboard() {
  const { data: all } = useLiveQuery(q => q.from({ todo: todoCollection }))
  const { data: pending } = useLiveQuery(q =>
    q.from({ todo: todoCollection }).where(({ todo }) => !todo.completed)
  )
  const { data: completed } = useLiveQuery(q =>
    q.from({ todo: todoCollection }).where(({ todo }) => todo.completed)
  )

  // All views update automatically when data changes
}
```

## Component architecture

### Composition patterns

Favor composition over prop drilling or complex context hierarchies.

```typescript
// Component composition with compound components
function DataTable({ children }: { children: React.ReactNode }) {
  const [sorting, setSorting] = useState<SortingState>([])

  return (
    <DataTableContext.Provider value={{ sorting, setSorting }}>
      {children}
    </DataTableContext.Provider>
  )
}

DataTable.Header = function Header() {
  const { sorting, setSorting } = useDataTableContext()
  // ...
}

DataTable.Body = function Body({ data }: { data: unknown[] }) {
  const { sorting } = useDataTableContext()
  // ...
}

// Usage
<DataTable>
  <DataTable.Header />
  <DataTable.Body data={users} />
</DataTable>
```

### Component libraries

**Preferred**: shadcn/ui with Radix UI primitives
- Copy-paste components for full control
- Accessible by default (Radix UI)
- Customizable with Tailwind CSS
- Type-safe composition

**Pattern**: Extend shadcn/ui components with domain logic

```typescript
// Extend shadcn Button with app-specific variants
import { Button } from '@/components/ui/button'
import { cva, type VariantProps } from 'class-variance-authority'

const appButtonVariants = cva('', {
  variants: {
    intent: {
      primary: 'bg-primary text-primary-foreground',
      danger: 'bg-destructive text-destructive-foreground',
      success: 'bg-green-600 text-white',
    },
  },
})

interface AppButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof appButtonVariants> {
  isLoading?: boolean
}

export function AppButton({ intent, isLoading, children, ...props }: AppButtonProps) {
  return (
    <Button className={appButtonVariants({ intent })} disabled={isLoading} {...props}>
      {isLoading ? <Spinner /> : children}
    </Button>
  )
}
```

### Avoid prop drilling

Use composition and context judiciously:
- **Composition first**: Pass components as children or props
- **Context for cross-cutting concerns**: Theme, auth, i18n
- **Not for data flow**: Use TanStack Router loaders instead

## Type-safe routing

TanStack Router provides compile-time type safety for routing.

### File-based routing conventions

```
routes/
  __root.tsx                    # Root layout
  index.tsx                     # / route
  users/
    index.tsx                   # /users
    $userId.tsx                 # /users/:userId
    $userId/
      edit.tsx                  # /users/:userId/edit
  (auth)/                       # Route group (no path segment)
    login.tsx                   # /login
    register.tsx                # /register
```

### Route definitions with loaders

```typescript
// routes/users/$userId.tsx
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'

// Validate search params
const userSearchSchema = z.object({
  tab: z.enum(['profile', 'settings', 'activity']).default('profile'),
})

export const Route = createFileRoute('/users/$userId')({
  // Validate params
  validateSearch: userSearchSchema,

  // Type-safe loader
  loader: async ({ params, context }) => {
    const user = await context.api.users.getById.query(params.userId)
    if (!user) throw new Error('User not found')
    return { user }
  },

  // Error boundary
  errorComponent: ({ error }) => <ErrorDisplay error={error} />,
})

function UserPage() {
  const { user } = Route.useLoaderData() // Typed as User!
  const search = Route.useSearch() // Typed from schema!
  const navigate = Route.useNavigate()

  // Type-safe navigation
  navigate({
    to: '/users/$userId',
    params: { userId: user.id },
    search: { tab: 'settings' }, // Type-checked!
  })
}
```

### Protected routes

```typescript
// routes/(protected)/_layout.tsx
export const Route = createFileRoute('/(protected)/_layout')({
  beforeLoad: async ({ context }) => {
    if (!context.auth.isAuthenticated) {
      throw redirect({ to: '/login' })
    }
  },
})
```

## Form handling

Integrate TanStack Form with Zod for type-safe validation and railway-oriented submission.

### Form patterns with railway-oriented programming

See @~/.claude/skills/preferences-railway-oriented-programming/SKILL.md for Result types and error composition.

```typescript
import { useForm } from '@tanstack/react-form'
import { zodValidator } from '@tanstack/zod-form-adapter'
import { z } from 'zod'

// Schema defines validation rules
const userSchema = z.object({
  email: z.string().email('Invalid email'),
  name: z.string().min(2, 'Name too short'),
  age: z.number().min(18, 'Must be 18+'),
})

type UserFormData = z.infer<typeof userSchema>

function UserForm() {
  const createUser = api.users.create.useMutation()

  const form = useForm({
    defaultValues: {
      email: '',
      name: '',
      age: 0,
    },
    onSubmit: async ({ value }) => {
      // Railway-oriented pipeline:
      // 1. Form validates (handled by TanStack Form + Zod)
      // 2. Submit to API (tRPC ensures type safety)
      // 3. Handle result
      const result = await createUser.mutateAsync(value)

      // Success track
      if (result.ok) {
        toast.success('User created')
        navigate({ to: '/users/$userId', params: { userId: result.data.id } })
      }
      // Failure track
      else {
        toast.error(result.error.message)
      }
    },
    validatorAdapter: zodValidator(),
  })

  return (
    <form onSubmit={(e) => {
      e.preventDefault()
      form.handleSubmit()
    }}>
      <form.Field
        name="email"
        validators={{
          onChange: userSchema.shape.email,
        }}
        children={(field) => (
          <div>
            <Input
              value={field.state.value}
              onChange={(e) => field.handleChange(e.target.value)}
            />
            {field.state.meta.errors && (
              <span className="text-destructive">{field.state.meta.errors[0]}</span>
            )}
          </div>
        )}
      />
      {/* Other fields... */}
      <Button type="submit" disabled={form.state.isSubmitting}>
        Submit
      </Button>
    </form>
  )
}
```

### Collecting all validation errors

Use applicative validation for better UX - show all errors at once.

See @~/.claude/skills/preferences-railway-oriented-programming/SKILL.md for applicative patterns.

```typescript
// TanStack Form validates all fields before submit
// Errors collected and displayed per-field
const form = useForm({
  validators: {
    // Run all validations, collect all errors
    onSubmit: userSchema,
  },
})
```

## Reactive patterns

### Avoiding unnecessary re-renders

```typescript
// ⊘ Bad: Subscribes to entire store
function BadComponent() {
  const store = useUIStore() // Re-renders on ANY state change
  return <div>{store.someValue}</div>
}

// ● Good: Subscribe to specific values
function GoodComponent() {
  const someValue = useUIStore((state) => state.someValue) // Only re-renders when someValue changes
  return <div>{someValue}</div>
}
```

### React Server Components (TanStack Start)

When using TanStack Start with SSR:
- Use server functions for data fetching where appropriate
- Minimize client-side JavaScript with Server Components
- Stream data for progressive enhancement

```typescript
// Server function
import { createServerFn } from '@tanstack/start'

export const getUser = createServerFn('GET', async (userId: string) => {
  // Runs only on server
  const user = await db.users.findById(userId)
  return user
})

// Client component can call server function
function UserProfile({ userId }: { userId: string }) {
  const user = await getUser(userId) // Type-safe server call
  return <div>{user.name}</div>
}
```

## Build tooling

### Vite with Rolldown

Prefer Rolldown over traditional Vite for faster builds:

```json
{
  "devDependencies": {
    "vite": "npm:rolldown-vite@latest"
  },
  "overrides": {
    "vite": "npm:rolldown-vite@latest"
  }
}
```

**Benefits**:
- Faster build times (Rust-based)
- Better tree-shaking
- Smaller bundle sizes

### Biome for linting and formatting

Use Biome instead of ESLint + Prettier for unified tooling.

**Configuration** (`biome.json`):

```json
{
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 120
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "style": {
        "useAsConstAssertion": "error",
        "noParameterAssign": "error"
      }
    }
  }
}
```

**Scripts**:

```json
{
  "scripts": {
    "format": "biome format --write",
    "lint": "biome lint",
    "check": "biome check",
    "check:fix": "biome check --write"
  }
}
```

## Package managers

### Bun vs pnpm

**Prefer Bun** for:
- Monorepo projects with local development
- Projects using Bun runtime features
- Faster installation and execution

**Use pnpm** for:
- Better compatibility with legacy packages
- Stricter dependency isolation
- Projects with complex peer dependencies

**Lock file convention**:
- Commit `bun.lockb` or `pnpm-lock.yaml`
- Never mix lock files in same project

## Styling

### Tailwind CSS patterns

Use Tailwind CSS v4 with the Vite plugin:

```typescript
// vite.config.ts
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [tailwindcss()],
})
```

**Import in entry**:

```typescript
// src/main.tsx or src/app.css
import 'tailwindcss'
```

### Component variants with CVA

Use `class-variance-authority` for type-safe variant management:

```typescript
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-md font-medium transition-colors',
  {
    variants: {
      variant: {
        default: 'bg-primary text-primary-foreground hover:bg-primary/90',
        destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
        outline: 'border border-input bg-background hover:bg-accent',
        ghost: 'hover:bg-accent hover:text-accent-foreground',
      },
      size: {
        default: 'h-10 px-4 py-2',
        sm: 'h-9 px-3',
        lg: 'h-11 px-8',
        icon: 'h-10 w-10',
      },
    },
    defaultVariants: {
      variant: 'default',
      size: 'default',
    },
  }
)

interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {}

export function Button({ className, variant, size, ...props }: ButtonProps) {
  return (
    <button
      className={cn(buttonVariants({ variant, size }), className)}
      {...props}
    />
  )
}
```

### Design tokens

Extract design tokens to CSS variables for consistency:

```css
@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --primary: 221.2 83.2% 53.3%;
    --primary-foreground: 210 40% 98%;
  }

  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;
    --primary: 217.2 91.2% 59.8%;
    --primary-foreground: 222.2 47.4% 11.2%;
  }
}
```

## Performance optimization

### Code splitting patterns

```typescript
// Route-based code splitting (automatic with TanStack Router)
// Each route component is lazy-loaded

// Component-based splitting
import { lazy, Suspense } from 'react'

const HeavyChart = lazy(() => import('./HeavyChart'))

function Dashboard() {
  return (
    <Suspense fallback={<ChartSkeleton />}>
      <HeavyChart data={data} />
    </Suspense>
  )
}
```

### Bundle optimization with Rolldown

```typescript
// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor-react': ['react', 'react-dom'],
          'vendor-tanstack': [
            '@tanstack/react-router',
            '@tanstack/react-query',
          ],
          'vendor-ui': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
        },
      },
    },
  },
})
```

### Image optimization

```typescript
// Use modern formats with fallbacks
<picture>
  <source srcSet="/image.avif" type="image/avif" />
  <source srcSet="/image.webp" type="image/webp" />
  <img src="/image.jpg" alt="Description" loading="lazy" />
</picture>
```

## Deployment

See @~/.claude/skills/preferences-web-application-deployment/SKILL.md for comprehensive deployment guidance including:
- Cloudflare Workers/Pages deployment (preferred)
- Database configuration (D1, PostgreSQL)
- Wrangler configuration and platform bindings
- Multi-environment strategies
- Service bindings for microservices architecture

### React-specific deployment patterns

#### Vite configuration with Cloudflare plugin

```typescript
// vite.config.ts
import { defineConfig } from "vite";
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import viteReact from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { cloudflare } from "@cloudflare/vite-plugin";

export default defineConfig({
  plugins: [
    tailwindcss(),
    tanstackStart({
      srcDirectory: "src",
      start: { entry: "./start.tsx" },
      server: { entry: "./server.ts" }, // Custom Cloudflare Workers entry
    }),
    viteReact(),
    cloudflare({
      viteEnvironment: {
        name: "ssr", // Enable SSR environment for Workers
      },
    }),
  ],
});
```

#### SSR hydration considerations

**Environment variables**:
- Avoid `VITE_` prefix for secrets - they bundle into client code
- Access runtime environment via Cloudflare bindings in server functions

```typescript
import { env } from "cloudflare:workers";

// Server function with runtime access
export const serverFunction = createServerFn()
  .handler(async () => {
    // Access Cloudflare bindings
    const data = await env.DB.prepare("SELECT * FROM users").all();
    return data;
  });
```

**Hydration**:
- Ensure server and client render identical markup
- Use `suppressHydrationWarning` only for intentional mismatches (timestamps, randomness)

```typescript
// Prevent hydration mismatch for client-only content
function ClientOnly({ children }: { children: React.ReactNode }) {
  const [mounted, setMounted] = useState(false)

  useEffect(() => {
    setMounted(true)
  }, [])

  if (!mounted) return null
  return <>{children}</>
}

// Usage: Wrap client-only components
<ClientOnly>
  <BrowserOnlyFeature />
</ClientOnly>
```

**Common hydration issues**:
- Different timestamps between server and client renders
- Browser-specific APIs called during SSR (window, localStorage)
- Random values generated server-side not matching client-side

```typescript
// ⊘ Causes hydration mismatch
function BadComponent() {
  return <div>{Math.random()}</div>
}

// ● Use suppressHydrationWarning for intentional mismatch
function GoodComponent() {
  return <div suppressHydrationWarning>{Math.random()}</div>
}

// ● Or use ClientOnly wrapper
function BestComponent() {
  return (
    <ClientOnly>
      <div>{Math.random()}</div>
    </ClientOnly>
  )
}
```

## Integration with backend patterns

### tRPC bridges frontend and backend

tRPC provides end-to-end type safety from database to UI.

**Server procedure**:

```typescript
// server/routes/users.ts
import { publicProcedure, router } from '../trpc'
import { z } from 'zod'

export const usersRouter = router({
  getById: publicProcedure
    .input(z.string().uuid())
    .query(async ({ input, ctx }) => {
      const user = await ctx.db.users.findById(input)
      if (!user) {
        throw new TRPCError({ code: 'NOT_FOUND' })
      }
      return user
    }),

  create: publicProcedure
    .input(z.object({
      email: z.string().email(),
      name: z.string().min(2),
    }))
    .mutation(async ({ input, ctx }) => {
      const user = await ctx.db.users.create(input)
      return user
    }),
})
```

**Client usage**:

```typescript
// Fully typed - no manual type definitions needed!
function UserProfile({ userId }: { userId: string }) {
  const { data: user, isLoading } = api.users.getById.useQuery(userId)
  const createUser = api.users.create.useMutation()

  // TypeScript knows:
  // - user type from server return type
  // - createUser input type from server input schema
  // - Error types from TRPCError
}
```

### Where to apply Effect-TS in UI layer

Reserve Effect-TS for complex client-side effects that benefit from explicit effect management:

```typescript
import { Effect } from 'effect'

// Example: Complex form submission with retries and fallbacks
const submitFormWithRetry = (data: FormData) =>
  Effect.gen(function* (_) {
    // Attempt primary API
    const result = yield* _(
      Effect.tryPromise({
        try: () => api.users.create.mutate(data),
        catch: (error) => new SubmitError({ cause: error }),
      })
    )

    return result
  }).pipe(
    // Retry with exponential backoff
    Effect.retry({
      times: 3,
      schedule: Schedule.exponential('100 millis'),
    }),
    // Fallback to queue for offline
    Effect.catchAll(() =>
      Effect.succeed(queueForLater(data))
    )
  )
```

Most UI code should use simpler patterns (TanStack Query, tRPC) unless:
- Complex retry/fallback logic required
- Multiple dependent effects need composition
- Advanced concurrency control needed

See @~/.claude/skills/preferences-typescript-nodejs-development/SKILL.md for Effect-TS patterns in backend code.

## Testing UI components

### Testing Library patterns

```typescript
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

describe('UserForm', () => {
  it('validates all fields and shows errors', async () => {
    const user = userEvent.setup()
    render(<UserForm />)

    // Submit without filling fields
    await user.click(screen.getByRole('button', { name: /submit/i }))

    // All validation errors should appear
    await waitFor(() => {
      expect(screen.getByText(/invalid email/i)).toBeInTheDocument()
      expect(screen.getByText(/name too short/i)).toBeInTheDocument()
      expect(screen.getByText(/must be 18\+/i)).toBeInTheDocument()
    })
  })
})
```

### Testing with TanStack Query

```typescript
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

function createWrapper() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
    },
  })

  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  )
}

describe('UserList', () => {
  it('fetches and displays users', async () => {
    render(<UserList />, { wrapper: createWrapper() })

    await waitFor(() => {
      expect(screen.getByText('John Doe')).toBeInTheDocument()
    })
  })
})
```

## Integration with algebraic data types

See @~/.claude/skills/preferences-algebraic-data-types/SKILL.md for ADT patterns in TypeScript.

**Use discriminated unions for component state machines**:

```typescript
// Form state as sum type
type FormState =
  | { status: 'idle' }
  | { status: 'validating' }
  | { status: 'submitting' }
  | { status: 'success'; data: User }
  | { status: 'error'; error: Error }

function FormComponent() {
  const [state, setState] = useState<FormState>({ status: 'idle' })

  // Pattern match on state
  switch (state.status) {
    case 'idle':
      return <FormFields onSubmit={handleSubmit} />
    case 'validating':
      return <FormFields disabled />
    case 'submitting':
      return <FormFields disabled><Spinner /></FormFields>
    case 'success':
      return <SuccessMessage user={state.data} />
    case 'error':
      return <ErrorMessage error={state.error} />
  }
}
```

**Use branded types for type-safe IDs**:

```typescript
import { Brand } from '@/lib/utils'

type UserId = Brand<string, 'UserId'>
type PostId = Brand<string, 'PostId'>

// Type error if you pass PostId to function expecting UserId
function getUser(id: UserId): Promise<User> { /* ... */ }
function getPost(id: PostId): Promise<Post> { /* ... */ }
```

Related Skills

project-development

16
from diegosouzapw/awesome-omni-skill

This skill should be used when the user asks to "start an LLM project", "design batch pipeline", "evaluate task-model fit", "structure agent project", or mentions pipeline architecture, agent-assisted development, cost estimation, or choosing between LLM and traditional approaches.

preferences-distributed-systems

16
from diegosouzapw/awesome-omni-skill

Distributed systems patterns including consistency models, consensus, and fault tolerance. Load when designing or debugging distributed architectures.

overnight-development

16
from diegosouzapw/awesome-omni-skill

Automates software development overnight using git hooks to enforce test-driven Use when appropriate context detected. Trigger with relevant phrases based on skill purpose.

nextjs-tanstack-form

16
from diegosouzapw/awesome-omni-skill

TanStack Form v1 for Next.js 16 with Server Actions, Zod validation, and shadcn/ui integration. Use when building forms, validation, multi-step wizards, or dynamic field arrays.

my-react-rules

16
from diegosouzapw/awesome-omni-skill

This is a new rule

mongodb-development

16
from diegosouzapw/awesome-omni-skill

MongoDB development guidelines with Payload CMS, Mongoose, aggregation pipelines, and TypeScript best practices.

monad-development

16
from diegosouzapw/awesome-omni-skill

Builds dapps on Monad blockchain. Use when deploying contracts, setting up frontends with viem/wagmi, or verifying contracts on Monad testnet or mainnet.

mobile_react_native

16
from diegosouzapw/awesome-omni-skill

React Native best practices, hooks, navigation ve performance optimization.

mobile-ui-development-rule

16
from diegosouzapw/awesome-omni-skill

General rules pertaining to Mobile UI development. Covers UI/UX best practices, state management, and navigation patterns.

mobile-development

16
from diegosouzapw/awesome-omni-skill

Cross-platform and native mobile development. Frameworks: React Native, Flutter, Swift/SwiftUI, Kotlin/Jetpack Compose. Capabilities: mobile UI, offline-first architecture, push notifications, deep linking, biometrics, app store deployment. Actions: build, create, implement, optimize, test, deploy mobile apps. Keywords: iOS, Android, React Native, Flutter, Swift, Kotlin, mobile app, offline sync, push notification, deep link, biometric auth, App Store, Play Store, iOS HIG, Material Design, battery optimization, memory management, mobile performance. Use when: building mobile apps, implementing mobile-first UX, choosing native vs cross-platform, optimizing battery/memory/network, deploying to app stores, handling mobile-specific features.

miniprogram-development

16
from diegosouzapw/awesome-omni-skill

WeChat Mini Program development rules. Use this skill when developing WeChat mini programs, integrating CloudBase capabilities, and deploying mini program projects.

minimalist-surgical-development

16
from diegosouzapw/awesome-omni-skill

Use when editing an existing codebase and the goal is minimal, standard, and non-invasive changes - prioritizes simplest solution, standard libraries first, and surgical modification without unsolicited refactors