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
- Single Responsibility: Each component does one thing well
- Composition over Inheritance: Build complex UIs from simple parts
- Props Down, Events Up: Data flows down, events bubble up
- Controlled Components: Parent owns the state when needed
- 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
- Controlled inputs for complex validation
- Uncontrolled inputs for simple forms (performance)
- Real-time validation for critical fields
- Submit validation for complete form
- 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:
- Component Implementation: Working component code
- State Management Setup: Store configuration and hooks
- API Integration Layer: Service classes and hooks
- Form Implementation: Forms with validation
- Performance Audit: Optimization recommendations
- Test Suite: Component and integration tests
- Project Structure: Folder organization
Weekly Installs
1
Repository
navedanan/backg…-removerFirst Seen
13 days ago
Security Audits
Installed on
windsurf1
amp1
cline1
opencode1
cursor1
kimi-cli1