---
name: frontend-dev-guidelines
description: Frontend development guidelines for React/TypeScript applications. Modern patterns including Suspense, lazy loading, useSuspenseQuery, file organization with features directory, styling best practices, routing, performance optimization, and TypeScript best practices. Use when creating components, pages, features, fetching data, styling, routing, or working with frontend code.
---
# Frontend Development Guidelines
## Project Structure
```
src/
├── features/ # Feature-based organization
│ ├── auth/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── api/
│ │ └── index.ts
│ ├── dashboard/
│ └── settings/
├── components/ # Shared/common components
│ ├── ui/ # Base UI components
│ └── layout/ # Layout components
├── hooks/ # Shared hooks
├── utils/ # Utility functions
├── types/ # TypeScript types
├── api/ # API layer
└── styles/ # Global styles
```
## Component Patterns
### Functional Components with TypeScript
```tsx
interface Props {
title: string;
onAction: (id: string) => void;
isLoading?: boolean;
}
export function MyComponent({ title, onAction, isLoading = false }: Props) {
// Component logic
return (
{isLoading ? : }
);
}
```
### Suspense for Data Fetching
```tsx
import { Suspense } from 'react';
function ParentComponent() {
return (
}>
);
}
function DataComponent() {
// useSuspenseQuery automatically suspends
const { data } = useSuspenseQuery({
queryKey: ['items'],
queryFn: fetchItems,
});
return ;
}
```
### Lazy Loading Routes
```tsx
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./features/dashboard'));
const Settings = lazy(() => import('./features/settings'));
function App() {
return (
}>
} />
} />
);
}
```
## State Management
### Local State
```tsx
const [value, setValue] = useState('');
const [items, setItems] = useState- ([]);
```
### Server State (TanStack Query)
```tsx
// Queries
const { data, isLoading, error } = useQuery({
queryKey: ['users', userId],
queryFn: () => fetchUser(userId),
});
// Mutations
const mutation = useMutation({
mutationFn: updateUser,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
```
### Global State (Zustand or Context)
```tsx
// Simple global state with Zustand
const useStore = create((set) => ({
theme: 'light',
setTheme: (theme) => set({ theme }),
}));
```
## Styling Best Practices
### Tailwind CSS
```tsx
{title}
```
### CSS Modules (when needed)
```tsx
import styles from './Component.module.css';
{title}
```
### Styled Components / Emotion (if used)
```tsx
const Container = styled.div`
display: flex;
align-items: center;
padding: ${({ theme }) => theme.spacing.md};
`;
```
## Performance Optimization
### Memoization
```tsx
// Memoize expensive computations
const expensiveValue = useMemo(() => {
return computeExpensiveValue(data);
}, [data]);
// Memoize callbacks
const handleClick = useCallback((id: string) => {
onAction(id);
}, [onAction]);
// Memoize components
const MemoizedComponent = memo(function Component({ data }: Props) {
return {data.value}
;
});
```
### Code Splitting
```tsx
// Route-based splitting
const Page = lazy(() => import('./Page'));
// Component-based splitting
const HeavyComponent = lazy(() => import('./HeavyComponent'));
```
### Virtual Lists
```tsx
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }: { items: Item[] }) {
const parentRef = useRef(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
});
return (
{virtualizer.getVirtualItems().map((virtualItem) => (
{items[virtualItem.index].name}
))}
);
}
```
## TypeScript Best Practices
### Type Definitions
```tsx
// Props interfaces
interface ButtonProps {
variant: 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
onClick: () => void;
children: React.ReactNode;
}
// API response types
interface ApiResponse {
data: T;
meta: {
page: number;
total: number;
};
}
// Utility types
type PartialUser = Partial;
type RequiredUser = Required;
type UserKeys = keyof User;
```
### Strict Null Checks
```tsx
// Handle potentially null values
const user = useUser();
if (!user) {
return ;
}
// Now TypeScript knows user is defined
return ;
```
## Error Handling
### Error Boundaries
```tsx
import { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
return (
Something went wrong:
{error.message}
);
}
```
## Testing
### Component Tests
```tsx
import { render, screen, fireEvent } from '@testing-library/react';
describe('Button', () => {
it('calls onClick when clicked', () => {
const onClick = vi.fn();
render();
fireEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledOnce();
});
});
```
### Hook Tests
```tsx
import { renderHook, act } from '@testing-library/react';
describe('useCounter', () => {
it('increments count', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});
```
## Resource Files
For detailed patterns, see:
- [component-patterns.md](resources/component-patterns.md)
- [state-management.md](resources/state-management.md)
- [performance.md](resources/performance.md)
- [testing.md](resources/testing.md)