Skip to content

Feature Components

Last Updated: 2025-01-22

Feature components are application-specific components that compose UI primitives to create reusable business features.


Overview

Feature components:

  • Combine UI components
  • Implement business logic
  • Handle user interactions
  • Integrate with Server Actions
  • Provide reusable patterns

Component Patterns

Server Component (Default)

// components/property-list.tsx
import { PropertyCard } from '@/components/property-card'
import type { PropertyListItem } from '@/lib/types'

interface PropertyListProps {
  properties: PropertyListItem[]
}

export function PropertyList({ properties }: PropertyListProps) {
  if (properties.length === 0) {
    return <div>No properties found</div>
  }

  return (
    <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
      {properties.map((property) => (
        <PropertyCard key={property.id} property={property} />
      ))}
    </div>
  )
}

Client Component

// components/search-input.tsx
'use client'

import { useState } from 'react'
import { Input } from '@/components/ui/input'
import { useDebouncedCallback } from 'use-debounce'

interface SearchInputProps {
  onSearch: (query: string) => void
  debounce?: number
}

export function SearchInput({ onSearch, debounce = 300 }: SearchInputProps) {
  const [query, setQuery] = useState('')

  const debouncedSearch = useDebouncedCallback(
    (value: string) => {
      onSearch(value)
    },
    debounce
  )

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    const value = e.target.value
    setQuery(value)
    debouncedSearch(value)
  }

  return (
    <Input
      type="search"
      placeholder="Search properties..."
      value={query}
      onChange={handleChange}
    />
  )
}

Composition

// components/dashboard-header.tsx
import { SearchInput } from '@/components/search-input'
import { UserNav } from '@/components/user-nav'

interface DashboardHeaderProps {
  user: {
    name: string
    email: string
  }
  onSearch: (query: string) => void
}

export function DashboardHeader({ user, onSearch }: DashboardHeaderProps) {
  return (
    <header className="flex items-center justify-between border-b px-6 py-4">
      <div className="flex-1">
        <SearchInput onSearch={onSearch} />
      </div>
      <UserNav user={user} />
    </header>
  )
}

Best Practices

  • Keep components focused and single-purpose
  • Use TypeScript for props
  • Extract reusable logic to hooks
  • Compose from UI primitives
  • Add loading and error states
  • Include accessibility attributes
  • Test component behavior