frontend-developer

SKILL.md

Frontend Developer

Expert guidance for building robust, performant, and maintainable frontend applications.

When to Use This Skill

  • Implementing UI components
  • Setting up frontend architecture
  • Managing application state
  • Handling forms and validation
  • Optimizing performance
  • Ensuring cross-browser compatibility
  • Integrating with backend APIs
  • Writing client-side logic

Component Architecture

Component Design Principles

  1. Single Responsibility: Each component does one thing well
  2. Composition over Inheritance: Build complex UIs from simple parts
  3. Props Down, Events Up: Data flows down, events bubble up
  4. Controlled Components: Parent owns the state when needed
  5. Separation of Concerns: UI, logic, and data access separated

Component Categories

├── ui/              # Pure presentational components
│   ├── Button
│   ├── Input
│   ├── Card
│   └── Modal
├── features/        # Feature-specific components
│   ├── UserProfile
│   ├── ProductCard
│   └── CheckoutForm
├── layouts/         # Page layout components
│   ├── Header
│   ├── Sidebar
│   └── PageContainer
└── pages/           # Route-level components
    ├── HomePage
    ├── ProductPage
    └── SettingsPage

Component File Structure

ComponentName/
├── index.ts              # Public exports
├── ComponentName.tsx     # Main component
├── ComponentName.test.ts # Tests
├── ComponentName.styles.css  # Styles (or .module.css)
├── ComponentName.types.ts    # TypeScript types
└── hooks/                # Component-specific hooks
    └── useComponentLogic.ts

State Management Patterns

Local State (Component Level)

Use for:

  • UI state (open/closed, selected tab)
  • Form inputs
  • Temporary data
// Simple state
const [isOpen, setIsOpen] = useState(false);

// Complex state with reducer
const [state, dispatch] = useReducer(reducer, initialState);

Shared State (Feature Level)

Use for:

  • Data shared between related components
  • Feature-specific state
  • Lift state to common ancestor
// Context for feature-level sharing
const FeatureContext = createContext<FeatureState>(defaultState);

function FeatureProvider({ children }) {
  const [state, setState] = useState(initialState);
  return (
    <FeatureContext.Provider value={{ state, setState }}>
      {children}
    </FeatureContext.Provider>
  );
}

Global State (Application Level)

Use for:

  • User authentication
  • App-wide settings/preferences
  • Cached server data

Options:

  • Context + useReducer: Simple apps
  • Zustand/Jotai: Medium complexity
  • Redux Toolkit: Large apps with complex state
  • React Query/SWR: Server state management

Server State

Use dedicated libraries for server data:

// React Query example
const { data, isLoading, error } = useQuery({
  queryKey: ['users', userId],
  queryFn: () => fetchUser(userId),
  staleTime: 5 * 60 * 1000, // 5 minutes
});

Form Handling

Form Best Practices

  1. Controlled inputs for complex validation
  2. Uncontrolled inputs for simple forms (performance)
  3. Real-time validation for critical fields
  4. Submit validation for complete form
  5. Accessible error messages linked to inputs

Form Validation Pattern

interface FormErrors {
  [fieldName: string]: string | undefined;
}

function validateForm(values: FormValues): FormErrors {
  const errors: FormErrors = {};
  
  if (!values.email) {
    errors.email = 'Email is required';
  } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) {
    errors.email = 'Invalid email address';
  }
  
  if (!values.password) {
    errors.password = 'Password is required';
  } else if (values.password.length < 8) {
    errors.password = 'Password must be at least 8 characters';
  }
  
  return errors;
}

Form Libraries

  • React Hook Form: Performance-focused, minimal re-renders
  • Formik: Full-featured, great for complex forms
  • Zod/Yup: Schema validation integration

API Integration

Fetch Wrapper

class ApiClient {
  private baseUrl: string;
  
  constructor(baseUrl: string) {
    this.baseUrl = baseUrl;
  }
  
  async request<T>(endpoint: string, options?: RequestInit): Promise<T> {
    const url = `${this.baseUrl}${endpoint}`;
    
    const config: RequestInit = {
      ...options,
      headers: {
        'Content-Type': 'application/json',
        ...options?.headers,
      },
    };
    
    // Add auth token if available
    const token = getAuthToken();
    if (token) {
      config.headers = {
        ...config.headers,
        Authorization: `Bearer ${token}`,
      };
    }
    
    const response = await fetch(url, config);
    
    if (!response.ok) {
      throw new ApiError(response.status, await response.text());
    }
    
    return response.json();
  }
  
  get<T>(endpoint: string) {
    return this.request<T>(endpoint, { method: 'GET' });
  }
  
  post<T>(endpoint: string, data: unknown) {
    return this.request<T>(endpoint, {
      method: 'POST',
      body: JSON.stringify(data),
    });
  }
  
  put<T>(endpoint: string, data: unknown) {
    return this.request<T>(endpoint, {
      method: 'PUT',
      body: JSON.stringify(data),
    });
  }
  
  delete<T>(endpoint: string) {
    return this.request<T>(endpoint, { method: 'DELETE' });
  }
}

Error Handling

// Custom error class
class ApiError extends Error {
  constructor(
    public status: number,
    public message: string,
    public code?: string
  ) {
    super(message);
    this.name = 'ApiError';
  }
}

// Error boundary for React
class ErrorBoundary extends React.Component<Props, State> {
  state = { hasError: false, error: null };
  
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }
  
  render() {
    if (this.state.hasError) {
      return <ErrorFallback error={this.state.error} />;
    }
    return this.props.children;
  }
}

Performance Optimization

Rendering Optimization

// Memoize expensive components
const ExpensiveComponent = React.memo(({ data }) => {
  // Only re-renders when data changes
  return <ComplexVisualization data={data} />;
});

// Memoize expensive calculations
const sortedItems = useMemo(
  () => items.sort((a, b) => a.name.localeCompare(b.name)),
  [items]
);

// Memoize callbacks passed to children
const handleClick = useCallback(
  (id: string) => {
    dispatch({ type: 'SELECT', payload: id });
  },
  [dispatch]
);

Code Splitting

// Route-based splitting
const ProductPage = lazy(() => import('./pages/ProductPage'));
const SettingsPage = lazy(() => import('./pages/SettingsPage'));

// Component-based splitting
const HeavyChart = lazy(() => import('./components/HeavyChart'));

// Usage with Suspense
<Suspense fallback={<LoadingSpinner />}>
  <HeavyChart data={chartData} />
</Suspense>

Image Optimization

// Lazy loading images
<img
  src={imageSrc}
  alt={description}
  loading="lazy"
  decoding="async"
  width={300}
  height={200}
/>

// Responsive images
<picture>
  <source
    srcSet="/image-large.webp"
    media="(min-width: 1024px)"
    type="image/webp"
  />
  <source
    srcSet="/image-medium.webp"
    media="(min-width: 640px)"
    type="image/webp"
  />
  <img src="/image-small.jpg" alt={description} />
</picture>

Performance Checklist

  • Bundle size analyzed and optimized
  • Code splitting implemented for routes
  • Images optimized and lazy loaded
  • Expensive computations memoized
  • Virtual scrolling for long lists
  • Debounce/throttle rapid events
  • Prefetch critical resources
  • Service worker for caching

Responsive Implementation

CSS Container Queries

/* Modern responsive components */
.card-container {
  container-type: inline-size;
}

.card {
  display: grid;
  gap: 1rem;
}

@container (min-width: 400px) {
  .card {
    grid-template-columns: 150px 1fr;
  }
}

Responsive Patterns

/* Fluid typography */
.heading {
  font-size: clamp(1.5rem, 4vw, 3rem);
}

/* Fluid spacing */
.section {
  padding: clamp(1rem, 5vw, 3rem);
}

/* Auto-fit grid */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.5rem;
}

Cross-Browser Compatibility

Feature Detection

// Check for feature support
if ('IntersectionObserver' in window) {
  // Use IntersectionObserver
} else {
  // Fallback behavior
}

// CSS feature queries
@supports (display: grid) {
  .container {
    display: grid;
  }
}

@supports not (gap: 1rem) {
  .container > * + * {
    margin-top: 1rem;
  }
}

Polyfills Strategy

// Dynamic polyfill loading
async function loadPolyfills() {
  const polyfills = [];
  
  if (!('fetch' in window)) {
    polyfills.push(import('whatwg-fetch'));
  }
  
  if (!('IntersectionObserver' in window)) {
    polyfills.push(import('intersection-observer'));
  }
  
  await Promise.all(polyfills);
}

Testing Strategy

Component Testing

// Testing library example
import { render, screen, fireEvent } from '@testing-library/react';

describe('Button', () => {
  it('calls onClick when clicked', () => {
    const handleClick = jest.fn();
    render(<Button onClick={handleClick}>Click me</Button>);
    
    fireEvent.click(screen.getByRole('button'));
    
    expect(handleClick).toHaveBeenCalledTimes(1);
  });
  
  it('is disabled when loading', () => {
    render(<Button loading>Submit</Button>);
    
    expect(screen.getByRole('button')).toBeDisabled();
  });
});

Hook Testing

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);
  });
});

Project Structure

src/
├── components/          # Reusable UI components
│   ├── ui/             # Primitive components
│   └── features/       # Feature components
├── hooks/              # Custom React hooks
├── services/           # API and external services
├── stores/             # Global state management
├── utils/              # Utility functions
├── types/              # TypeScript type definitions
├── styles/             # Global styles and themes
├── constants/          # App constants
├── pages/              # Route components
└── App.tsx             # Root component

Output Artifacts

When this skill is activated, I can help create:

  1. Component Implementation: Working component code
  2. State Management Setup: Store configuration and hooks
  3. API Integration Layer: Service classes and hooks
  4. Form Implementation: Forms with validation
  5. Performance Audit: Optimization recommendations
  6. Test Suite: Component and integration tests
  7. Project Structure: Folder organization
Weekly Installs
1
First Seen
13 days ago
Installed on
windsurf1
amp1
cline1
opencode1
cursor1
kimi-cli1