# Component Development Guidelines
This document covers component development patterns including Server vs Client components, semantic HTML, and UI best practices.
## Server vs Client Components
### Default to Server Components
Next.js App Router defaults to Server Components. Use them for:
- Data fetching
- Accessing backend resources directly
- Keeping sensitive data on the server
- Reducing client-side JavaScript
```typescript
// app/(app)/dashboard/page.tsx (Server Component)
import { DashboardStats } from '@/modules/dashboard/components';
export default async function DashboardPage() {
// Can fetch data directly
const stats = await fetchDashboardStats();
return (
Dashboard
);
}
```
### When to Use Client Components
Add `'use client'` directive only when you need:
- Event handlers (onClick, onChange, etc.)
- useState, useEffect, or other React hooks
- Browser-only APIs (localStorage, window)
- Class components with lifecycle methods
```typescript
'use client';
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
);
}
```
### Composition Pattern
Keep Server Components at the top, push Client Components down:
```typescript
// Server Component (page.tsx)
import { ProductList } from './ProductList';
import { FilterSidebar } from './FilterSidebar'; // Client
export default async function ProductsPage() {
const products = await fetchProducts();
return (
{/* Client component for interactivity */}
{/* Can be server or client */}
);
}
```
### Passing Server Data to Client Components
```typescript
// Server Component
export default async function Page() {
const initialData = await fetchData();
return ;
}
// Client Component
'use client';
export function InteractiveWidget({ initialData }: { initialData: Data }) {
const [data, setData] = useState(initialData);
// Interactive logic...
}
```
## Semantic HTML
## Kumo UI Convention
Use `components/ui/*` as the project-owned adapter layer for Cloudflare Kumo components.
Feature modules and route files should import `Button`, `Input`, `Select`, `Textarea`, `Table`,
`Checkbox`, `Badge`, `Card`, and `Dialog` from `@/components/ui/...` instead of importing
`@cloudflare/kumo` directly.
```typescript
// Good: project adapter keeps Kumo API differences local
import { Select } from "@/components/ui/select";
import { Table } from "@/components/ui/table";
// Avoid: feature code should not hand-style native controls
```
Visible form controls and data tables should use the Kumo-backed adapters. Native hidden
inputs are acceptable only inside adapters when needed to preserve FormData semantics.
### Select Adapter Positioning Contract
`components/ui/select.tsx` is intentionally project-owned instead of directly wrapping
`@cloudflare/kumo/components/select`. Kumo Select 2.6 can inherit Base UI
`alignItemWithTrigger=true`, which may render `data-side="none"` and overlap the trigger in
production.
The project Select adapter must:
- Preserve the local API: `options`, `value`, `defaultValue`, `name`, `placeholder`,
`disabled`, `required`, `aria-*`, and `onValueChange`.
- Render a hidden form value only inside the adapter when `name` is provided.
- Use portal/fixed positioning for the popup and keep a positive gap between trigger and
panel. Browser validation should assert `verticalOverlap === 0` on desktop and mobile.
- Keep keyboard behavior for ArrowUp/ArrowDown, Home/End, Enter/Space, Escape, and Tab.
```typescript
// Good: feature code stays on the project adapter.
// Avoid: this can reintroduce production overlay regressions.
import { Select } from "@cloudflare/kumo/components/select";
```
### Dialog Adapter Centering Contract
`components/ui/dialog.tsx` may use Kumo Dialog primitives for accessibility, but the
project adapter owns panel geometry. Kumo Dialog includes fixed `left-1/2 top-1/2`
and translate classes, and those transforms can interact with project animations or
production CSS ordering.
The project Dialog adapter must:
- Center the panel without relying on translate transforms. Use `fixed inset-0 m-auto`
plus explicit width, max-height, and `h-fit`.
- Override Kumo minimum width so mobile dialogs stay inside `100vw`.
- Keep `transform: none` and `translate: 0 0` on `.teatea-dialog`; use
opacity/scale-only motion if animation is needed.
- Browser validation should assert the panel center is within 2px of the viewport center
on desktop and mobile.
### Use Proper Elements
```typescript
// Bad: div for everything