Modern Frontend Development - UI/UX & Performance

Comprehensive guide covering essential concepts, practical examples, and best practices. Learn with step-by-step tutorials and real-world applications.

Back to Articles

The Modern Frontend Landscape

Frontend development has evolved from simple HTML pages to complex, interactive applications. Modern frontend development encompasses user interface design, user experience optimization, performance considerations, and maintainable code architecture.

"The best interface is no interface, but when you need one, make it feel invisible." - Golden Krishna

Frontend Frameworks Comparison

React

  • Component-based architecture
  • Virtual DOM for performance
  • Huge ecosystem and community
  • Flexible and unopinionated
  • JSX syntax

Vue.js

  • Progressive framework
  • Template-based syntax
  • Gentle learning curve
  • Built-in state management
  • Excellent documentation

Angular

  • Full-featured framework
  • TypeScript by default
  • Dependency injection
  • Comprehensive CLI tools
  • Enterprise-ready

React Development Deep Dive

Modern React Patterns

React Hooks and State Management

import React, { useState, useEffect, useContext, useReducer } from 'react';

// Custom hook for API calls
function useApi(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        setError(null);
        const response = await fetch(url);
        
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };
    
    fetchData();
  }, [url]);
  
  return { data, loading, error };
}

// Context for state management
const UserContext = React.createContext();

function UserProvider({ children }) {
  const [user, setUser] = useState(null);
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  
  const login = async (credentials) => {
    try {
      const response = await fetch('/api/auth/login', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(credentials),
      });
      
      const data = await response.json();
      
      if (response.ok) {
        setUser(data.user);
        setIsAuthenticated(true);
        localStorage.setItem('token', data.token);
      } else {
        throw new Error(data.message);
      }
    } catch (error) {
      throw error;
    }
  };
  
  const logout = () => {
    setUser(null);
    setIsAuthenticated(false);
    localStorage.removeItem('token');
  };
  
  const value = {
    user,
    isAuthenticated,
    login,
    logout
  };
  
  return (
    <UserContext.Provider value={value}>
      {children}
    </UserContext.Provider>
  );
}

// Component using hooks
function UserProfile() {
  const { user, logout } = useContext(UserContext);
  const { data: posts, loading, error } = useApi(`/api/users/${user.id}/posts`);
  
  if (loading) return <div className="loading">Loading posts...</div>;
  if (error) return <div className="error">Error: {error}</div>;
  
  return (
    <div className="user-profile">
      <header className="profile-header">
        <h1>Welcome, {user.name}!</h1>
        <button onClick={logout} className="logout-btn">
          Logout
        </button>
      </header>
      
      <section className="user-posts">
        <h2>Your Posts ({posts?.length || 0})</h2>
        {posts?.map(post => (
          <article key={post.id} className="post-card">
            <h3>{post.title}</h3>
            <p>{post.excerpt}</p>
            <time>{new Date(post.createdAt).toLocaleDateString()}</time>
          </article>
        ))}
      </section>
    </div>
  );
}

React Performance Optimization

import React, { memo, useMemo, useCallback, lazy, Suspense } from 'react';

// Memoized component to prevent unnecessary re-renders
const PostCard = memo(({ post, onLike, onShare }) => {
  return (
    <div className="post-card">
      <h3>{post.title}</h3>
      <p>{post.content}</p>
      <div className="post-actions">
        <button onClick={() => onLike(post.id)}>
          ❤️ {post.likes}
        </button>
        <button onClick={() => onShare(post.id)}>
          📤 Share
        </button>
      </div>
    </div>
  );
});

// Lazy loading for code splitting
const UserSettings = lazy(() => import('./UserSettings'));
const Analytics = lazy(() => import('./Analytics'));

function PostList({ posts, searchTerm }) {
  // Memoize expensive calculations
  const filteredPosts = useMemo(() => {
    return posts.filter(post => 
      post.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
      post.content.toLowerCase().includes(searchTerm.toLowerCase())
    );
  }, [posts, searchTerm]);
  
  // Memoize callback functions
  const handleLike = useCallback((postId) => {
    // Like post logic
    console.log('Liked post:', postId);
  }, []);
  
  const handleShare = useCallback((postId) => {
    // Share post logic
    navigator.share({
      title: 'Check out this post',
      url: `/posts/${postId}`
    });
  }, []);
  
  return (
    <div className="post-list">
      {filteredPosts.map(post => (
        <PostCard
          key={post.id}
          post={post}
          onLike={handleLike}
          onShare={handleShare}
        />
      ))}
    </div>
  );
}

// App with lazy loading
function App() {
  const [activeTab, setActiveTab] = useState('posts');
  
  return (
    <div className="app">
      <nav>
        <button 
          onClick={() => setActiveTab('posts')}
          className={activeTab === 'posts' ? 'active' : ''}
        >
          Posts
        </button>
        <button 
          onClick={() => setActiveTab('settings')}
          className={activeTab === 'settings' ? 'active' : ''}
        >
          Settings
        </button>
        <button 
          onClick={() => setActiveTab('analytics')}
          className={activeTab === 'analytics' ? 'active' : ''}
        >
          Analytics
        </button>
      </nav>
      
      <main>
        {activeTab === 'posts' && <PostList />}
        
        {activeTab === 'settings' && (
          <Suspense fallback={<div>Loading settings...</div>}>
            <UserSettings />
          </Suspense>
        )}
        
        {activeTab === 'analytics' && (
          <Suspense fallback={<div>Loading analytics...</div>}>
            <Analytics />
          </Suspense>
        )}
      </main>
    </div>
  );
}

Vue.js Development

Vue 3 Composition API

Vue 3 with Composition API

<template>
  <div class="user-dashboard">
    <header class="dashboard-header">
      <h1>Dashboard</h1>
      <button @click="refreshData" :disabled="loading">
        {{ loading ? 'Loading...' : 'Refresh' }}
      </button>
    </header>
    
    <div class="filters">
      <input 
        v-model="searchTerm" 
        placeholder="Search users..."
        class="search-input"
      />
      <select v-model="statusFilter">
        <option value="">All Status</option>
        <option value="active">Active</option>
        <option value="inactive">Inactive</option>
      </select>
    </div>
    
    <div v-if="error" class="error">
      {{ error }}
    </div>
    
    <div class="user-grid">
      <div 
        v-for="user in filteredUsers" 
        :key="user.id" 
        class="user-card"
        :class="{ 'inactive': !user.isActive }"
      >
        <img :src="user.avatar" :alt="user.name" class="avatar" />
        <h3>{{ user.name }}</h3>
        <p>{{ user.email }}</p>
        <span class="status-badge" :class="user.status">
          {{ user.status }}
        </span>
        <button @click="toggleUserStatus(user.id)">
          {{ user.isActive ? 'Deactivate' : 'Activate' }}
        </button>
      </div>
    </div>
    
    <div v-if="filteredUsers.length === 0 && !loading" class="no-results">
      No users found matching your criteria.
    </div>
  </div>
</template>

<script>
import { ref, computed, onMounted, watch } from 'vue';
import { useUsers } from '@/composables/useUsers';
import { useNotifications } from '@/composables/useNotifications';

export default {
  name: 'UserDashboard',
  setup() {
    const searchTerm = ref('');
    const statusFilter = ref('');
    
    const { 
      users, 
      loading, 
      error, 
      fetchUsers, 
      updateUserStatus 
    } = useUsers();
    
    const { showNotification } = useNotifications();
    
    // Computed property for filtered users
    const filteredUsers = computed(() => {
      return users.value.filter(user => {
        const matchesSearch = user.name.toLowerCase()
          .includes(searchTerm.value.toLowerCase()) ||
          user.email.toLowerCase()
          .includes(searchTerm.value.toLowerCase());
          
        const matchesStatus = !statusFilter.value || 
          (statusFilter.value === 'active' && user.isActive) ||
          (statusFilter.value === 'inactive' && !user.isActive);
          
        return matchesSearch && matchesStatus;
      });
    });
    
    // Methods
    const refreshData = async () => {
      try {
        await fetchUsers();
        showNotification('Data refreshed successfully', 'success');
      } catch (err) {
        showNotification('Failed to refresh data', 'error');
      }
    };
    
    const toggleUserStatus = async (userId) => {
      try {
        await updateUserStatus(userId);
        showNotification('User status updated', 'success');
      } catch (err) {
        showNotification('Failed to update user status', 'error');
      }
    };
    
    // Lifecycle
    onMounted(() => {
      fetchUsers();
    });
    
    // Watch for search term changes
    watch(searchTerm, (newTerm) => {
      // Debounce search if needed
      console.log('Searching for:', newTerm);
    });
    
    return {
      searchTerm,
      statusFilter,
      users,
      loading,
      error,
      filteredUsers,
      refreshData,
      toggleUserStatus
    };
  }
};
</script>

<style scoped>
.user-dashboard {
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
}

.dashboard-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}

.filters {
  display: flex;
  gap: 15px;
  margin-bottom: 20px;
}

.search-input {
  flex: 1;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.user-grid {
  display: flex;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px;
}

.user-card {
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 20px;
  text-align: center;
  transition: transform 0.2s;
}

.user-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}

.user-card.inactive {
  opacity: 0.6;
}

.avatar {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  margin-bottom: 10px;
}

.status-badge {
  padding: 4px 8px;
  border-radius: 16px;
  font-size: 0.8em;
  font-weight: bold;
}

.status-badge.active {
  background: #d4edda;
  color: #155724;
}

.status-badge.inactive {
  background: #f8d7da;
  color: #721c24;
}
</style>

CSS and Styling Approaches

Modern CSS Techniques

/* CSS Custom Properties (Variables) */
:root {
  --primary-color: #007bff;
  --secondary-color: #6c757d;
  --success-color: #28a745;
  --danger-color: #dc3545;
  --warning-color: #ffc107;
  
  --font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto;
  --font-size-base: 1rem;
  --line-height-base: 1.5;
  
  --border-radius: 0.375rem;
  --box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
  --transition: all 0.15s ease-in-out;
}

/* CSS Grid Layout */
.dashboard-layout {
  display: flex;
  grid-template-areas: 
    "header header header"
    "sidebar main aside"
    "footer footer footer";
  grid-template-columns: 250px 1fr 300px;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
  gap: 1rem;
}

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }

/* Responsive Grid */
@media (max-width: 768px) {
  .dashboard-layout {
    grid-template-areas: 
      "header"
      "main"
      "sidebar"
      "aside"
      "footer";
    flex-direction: column;
  }
}

/* Flexbox for Components */
.card {
  display: flex;
  flex-direction: column;
  background: white;
  border-radius: var(--border-radius);
  box-shadow: var(--box-shadow);
  overflow: hidden;
  transition: var(--transition);
}

.card:hover {
  transform: translateY(-2px);
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}

.card-header {
  padding: 1rem;
  border-bottom: 1px solid #dee2e6;
  background: #f8f9fa;
}

.card-body {
  flex: 1;
  padding: 1rem;
}

.card-footer {
  padding: 0.75rem 1rem;
  background: #f8f9fa;
  border-top: 1px solid #dee2e6;
}

/* Modern Form Styling */
.form-group {
  margin-bottom: 1rem;
}

.form-control {
  width: 100%;
  padding: 0.375rem 0.75rem;
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  color: #495057;
  background: white;
  border: 1px solid #ced4da;
  border-radius: var(--border-radius);
  transition: var(--transition);
}

.form-control:focus {
  outline: 0;
  border-color: #80bdff;
  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}

/* CSS Animations */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.fade-in-up {
  animation: fadeInUp 0.5s ease-out;
}

/* Loading Animation */
.loading-spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid var(--primary-color);
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin: 0 auto;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

CSS-in-JS with Styled Components

import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';

// Global styles
const GlobalStyle = createGlobalStyle`
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }
  
  body {
    font-family: ${props => props.theme.fonts.primary};
    color: ${props => props.theme.colors.text};
    line-height: 1.6;
  }
`;

// Theme definition
const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
    success: '#28a745',
    danger: '#dc3545',
    text: '#212529',
    background: '#ffffff',
    border: '#dee2e6'
  },
  fonts: {
    primary: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto',
    monospace: 'Monaco, Consolas, "Liberation Mono", "Courier New"'
  },
  breakpoints: {
    mobile: '576px',
    tablet: '768px',
    desktop: '992px',
    wide: '1200px'
  }
};

// Styled components
const Container = styled.div`
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 1rem;
  
  @media (min-width: ${props => props.theme.breakpoints.tablet}) {
    padding: 0 2rem;
  }
`;

const Button = styled.button`
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 0.375rem;
  font-size: 1rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s ease-in-out;
  
  background: ${props => 
    props.variant === 'danger' ? props.theme.colors.danger :
    props.variant === 'success' ? props.theme.colors.success :
    props.theme.colors.primary
  };
  
  color: white;
  
  &:hover {
    opacity: 0.9;
    transform: translateY(-1px);
  }
  
  &:disabled {
    opacity: 0.6;
    cursor: not-allowed;
    transform: none;
  }
  
  ${props => props.size === 'large' && `
    padding: 0.75rem 1.5rem;
    font-size: 1.125rem;
  `}
  
  ${props => props.size === 'small' && `
    padding: 0.25rem 0.5rem;
    font-size: 0.875rem;
  `}
`;

const Card = styled.div`
  background: ${props => props.theme.colors.background};
  border: 1px solid ${props => props.theme.colors.border};
  border-radius: 0.5rem;
  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
  overflow: hidden;
  transition: all 0.15s ease-in-out;
  
  &:hover {
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
    transform: translateY(-2px);
  }
`;

const CardHeader = styled.div`
  padding: 1rem;
  background: #f8f9fa;
  border-bottom: 1px solid ${props => props.theme.colors.border};
  font-weight: 600;
`;

const CardBody = styled.div`
  padding: 1rem;
`;

// Usage in React component
function App() {
  return (
    <ThemeProvider theme={theme}>
      <GlobalStyle />
      <Container>
        <Card>
          <CardHeader>
            Welcome to Our App
          </CardHeader>
          <CardBody>
            <p>This is a styled component example.</p>
            <Button variant="primary" size="large">
              Get Started
            </Button>
            <Button variant="danger" size="small">
              Delete
            </Button>
          </CardBody>
        </Card>
      </Container>
    </ThemeProvider>
  );
}

UI/UX Design Principles

Visual Hierarchy

  • Use size, color, and spacing to guide attention
  • Establish clear focal points
  • Create scannable content with proper headings
  • Use contrast effectively

Accessibility

  • Semantic HTML for screen readers
  • Proper color contrast ratios
  • Keyboard navigation support
  • Alt text for images

Responsive Design

  • Mobile-first approach
  • Flexible grid systems
  • Scalable typography
  • Touch-friendly interactions

Accessibility Implementation

// Accessible React components
import React, { useState, useRef, useEffect } from 'react';

function AccessibleModal({ isOpen, onClose, title, children }) {
  const modalRef = useRef(null);
  const previousFocusRef = useRef(null);
  
  useEffect(() => {
    if (isOpen) {
      // Store the previously focused element
      previousFocusRef.current = document.activeElement;
      
      // Focus the modal
      modalRef.current?.focus();
      
      // Trap focus within modal
      const handleKeyDown = (e) => {
        if (e.key === 'Escape') {
          onClose();
        }
        
        if (e.key === 'Tab') {
          // Implement focus trap logic
          const focusableElements = modalRef.current.querySelectorAll(
            'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
          );
          
          const firstElement = focusableElements[0];
          const lastElement = focusableElements[focusableElements.length - 1];
          
          if (e.shiftKey && document.activeElement === firstElement) {
            e.preventDefault();
            lastElement.focus();
          } else if (!e.shiftKey && document.activeElement === lastElement) {
            e.preventDefault();
            firstElement.focus();
          }
        }
      };
      
      document.addEventListener('keydown', handleKeyDown);
      
      return () => {
        document.removeEventListener('keydown', handleKeyDown);
        // Restore focus to previously focused element
        previousFocusRef.current?.focus();
      };
    }
  }, [isOpen, onClose]);
  
  if (!isOpen) return null;
  
  return (
    <div 
      className="modal-overlay"
      onClick={onClose}
      role="dialog"
      aria-modal="true"
      aria-labelledby="modal-title"
    >
      <div 
        ref={modalRef}
        className="modal-content"
        onClick={(e) => e.stopPropagation()}
        tabIndex="-1"
      >
        <header className="modal-header">
          <h2 id="modal-title">{title}</h2>
          <button 
            onClick={onClose}
            aria-label="Close modal"
            className="close-button"
          >
            ×
          </button>
        </header>
        <div className="modal-body">
          {children}
        </div>
      </div>
    </div>
  );
}

// Accessible form component
function AccessibleForm({ onSubmit }) {
  const [formData, setFormData] = useState({
    email: '',
    password: '',
    confirmPassword: ''
  });
  const [errors, setErrors] = useState({});
  
  const handleSubmit = (e) => {
    e.preventDefault();
    
    // Validation logic
    const newErrors = {};
    
    if (!formData.email) {
      newErrors.email = 'Email is required';
    } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
      newErrors.email = 'Email is invalid';
    }
    
    if (!formData.password) {
      newErrors.password = 'Password is required';
    } else if (formData.password.length < 8) {
      newErrors.password = 'Password must be at least 8 characters';
    }
    
    if (formData.password !== formData.confirmPassword) {
      newErrors.confirmPassword = 'Passwords do not match';
    }
    
    setErrors(newErrors);
    
    if (Object.keys(newErrors).length === 0) {
      onSubmit(formData);
    } else {
      // Focus first field with error
      const firstErrorField = Object.keys(newErrors)[0];
      document.getElementById(firstErrorField)?.focus();
    }
  };
  
  return (
    <form onSubmit={handleSubmit} noValidate>
      <div className="form-group">
        <label htmlFor="email">
          Email Address *
        </label>
        <input
          id="email"
          type="email"
          value={formData.email}
          onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
          aria-describedby={errors.email ? 'email-error' : undefined}
          aria-invalid={!!errors.email}
          required
        />
        {errors.email && (
          <div id="email-error" role="alert" className="error-message">
            {errors.email}
          </div>
        )}
      </div>
      
      <div className="form-group">
        <label htmlFor="password">
          Password *
        </label>
        <input
          id="password"
          type="password"
          value={formData.password}
          onChange={(e) => setFormData(prev => ({ ...prev, password: e.target.value }))}
          aria-describedby={errors.password ? 'password-error' : 'password-help'}
          aria-invalid={!!errors.password}
          required
        />
        <div id="password-help" className="form-help">
          Password must be at least 8 characters long
        </div>
        {errors.password && (
          <div id="password-error" role="alert" className="error-message">
            {errors.password}
          </div>
        )}
      </div>
      
      <button type="submit">
        Create Account
      </button>
    </form>
  );
}

Performance Optimization

Loading Performance

  • Code splitting and lazy loading
  • Image optimization and lazy loading
  • Minimize bundle sizes
  • Efficient caching strategies

Runtime Performance

  • Virtual scrolling for large lists
  • Debounce user inputs
  • Optimize re-renders
  • Use web workers for heavy computations

Monitoring

  • Core Web Vitals tracking
  • Performance profiling tools
  • Real user monitoring (RUM)
  • Error tracking and logging

Performance Monitoring

// Performance monitoring utilities
class PerformanceMonitor {
  static measurePageLoad() {
    window.addEventListener('load', () => {
      const navigation = performance.getEntriesByType('navigation')[0];
      const paint = performance.getEntriesByType('paint');
      
      const metrics = {
        // Core Web Vitals
        FCP: paint.find(entry => entry.name === 'first-contentful-paint')?.startTime,
        LCP: 0, // Measured separately
        CLS: 0, // Measured separately
        FID: 0, // Measured separately
        
        // Navigation Timing
        domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
        loadComplete: navigation.loadEventEnd - navigation.loadEventStart,
        
        // Resource timing
        totalLoadTime: navigation.loadEventEnd - navigation.fetchStart,
        dnsTime: navigation.domainLookupEnd - navigation.domainLookupStart,
        connectTime: navigation.connectEnd - navigation.connectStart,
        responseTime: navigation.responseEnd - navigation.responseStart,
        domParseTime: navigation.domContentLoadedEventStart - navigation.responseEnd
      };
      
      // Send metrics to analytics
      this.sendMetrics(metrics);
    });
  }
  
  static measureLCP() {
    const observer = new PerformanceObserver((list) => {
      const entries = list.getEntries();
      const lastEntry = entries[entries.length - 1];
      
      this.sendMetric('LCP', lastEntry.startTime);
    });
    
    observer.observe({ entryTypes: ['largest-contentful-paint'] });
  }
  
  static measureCLS() {
    let clsValue = 0;
    let clsEntries = [];
    
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        if (!entry.hadRecentInput) {
          clsEntries.push(entry);
          clsValue += entry.value;
        }
      }
      
      this.sendMetric('CLS', clsValue);
    });
    
    observer.observe({ entryTypes: ['layout-shift'] });
  }
  
  static measureFID() {
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        this.sendMetric('FID', entry.processingStart - entry.startTime);
      }
    });
    
    observer.observe({ entryTypes: ['first-input'] });
  }
  
  static measureComponentRender(componentName, renderFunction) {
    const startTime = performance.now();
    
    const result = renderFunction();
    
    const endTime = performance.now();
    const renderTime = endTime - startTime;
    
    if (renderTime > 16) { // More than one frame
      console.warn(`Slow render detected: ${componentName} took ${renderTime.toFixed(2)}ms`);
    }
    
    this.sendMetric('componentRender', {
      component: componentName,
      duration: renderTime
    });
    
    return result;
  }
  
  static sendMetrics(metrics) {
    // Send to analytics service
    if (typeof gtag !== 'undefined') {
      Object.entries(metrics).forEach(([key, value]) => {
        gtag('event', 'performance_metric', {
          metric_name: key,
          metric_value: Math.round(value)
        });
      });
    }
    
    // Send to custom analytics
    fetch('/api/analytics/performance', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        url: window.location.href,
        userAgent: navigator.userAgent,
        metrics
      })
    }).catch(err => console.error('Failed to send performance metrics:', err));
  }
  
  static sendMetric(name, value) {
    this.sendMetrics({ [name]: value });
  }
}

// Initialize performance monitoring
PerformanceMonitor.measurePageLoad();
PerformanceMonitor.measureLCP();
PerformanceMonitor.measureCLS();
PerformanceMonitor.measureFID();

// Usage in React component
function MyComponent() {
  return PerformanceMonitor.measureComponentRender('MyComponent', () => {
    // Component rendering logic
    return (
      <div>
        <h1>My Component</h1>
        {/* Complex rendering logic */}
      </div>
    );
  });
}

Modern Development Tools

Build Tools

  • Vite: Fast build tool and dev server
  • Webpack: Module bundler with rich ecosystem
  • Parcel: Zero-configuration build tool
  • Rollup: Module bundler for libraries

Testing

  • Jest: JavaScript testing framework
  • React Testing Library: React component testing
  • Cypress: End-to-end testing
  • Playwright: Cross-browser testing

Development

  • TypeScript: Type safety for JavaScript
  • ESLint: Code quality and style checking
  • Prettier: Code formatting
  • Storybook: Component development environment

Conclusion

Modern frontend development requires balancing user experience, performance, accessibility, and maintainability. Choose the right tools and frameworks for your project, always prioritize user needs, and continuously optimize for performance.

Stay updated with the latest trends, practice building accessible interfaces, and remember that the best frontend is one that users barely notice because it works so seamlessly.