Learn system design concepts specifically for frontend engineers. This comprehensive guide covers CDN strategy, caching mechanisms, micro-frontend architecture, performance monitoring, and scalable component design patterns that are essential for building large-scale frontend systems.
Frontend system design involves architecting client-side applications that can handle millions of users, provide excellent performance, and remain maintainable as they scale. Unlike backend system design, frontend design focuses on user experience, performance optimization, and efficient resource utilization.
CDNs are crucial for delivering frontend assets efficiently to users worldwide.
// CDN Configuration Example
const CDN_CONFIG = {
production: {
staticAssets: 'https://cdn.example.com/static/',
images: 'https://images.cdn.example.com/',
videos: 'https://video.cdn.example.com/',
api: 'https://api.example.com/',
},
staging: {
staticAssets: 'https://staging-cdn.example.com/static/',
images: 'https://staging-images.cdn.example.com/',
videos: 'https://staging-video.cdn.example.com/',
api: 'https://staging-api.example.com/',
},
development: {
staticAssets: '/static/',
images: '/images/',
videos: '/videos/',
api: 'http://localhost:3001/',
},
};
export const getCDNUrl = (assetType, path) => {
const env = process.env.NODE_ENV || 'development';
const baseUrl = CDN_CONFIG[env][assetType];
return `${baseUrl}${path}`;
};
// Usage
const imageUrl = getCDNUrl('images', 'user-avatar.jpg');
const scriptUrl = getCDNUrl('staticAssets', 'js/main.bundle.js');
// Responsive Image Component with CDN
import React from 'react';
const OptimizedImage = ({
src,
alt,
sizes = '100vw',
loading = 'lazy',
...props
}) => {
const generateSrcSet = (baseSrc) => {
const formats = ['webp', 'jpg'];
const sizes = [320, 640, 960, 1280, 1920];
return formats.map(format =>
sizes.map(size =>
`${getCDNUrl('images', `${baseSrc}?w=${size}&f=${format}`)} ${size}w`
).join(', ')
);
};
return (
<picture>
<source
srcSet={generateSrcSet(src)[0]}
type="image/webp"
sizes={sizes}
/>
<img
src={getCDNUrl('images', src)}
srcSet={generateSrcSet(src)[1]}
alt={alt}
loading={loading}
sizes={sizes}
{...props}
/>
</picture>
);
};
export default OptimizedImage;
// Cache Control Headers Configuration
const CACHE_STRATEGIES = {
staticAssets: {
'Cache-Control': 'public, max-age=31536000, immutable', // 1 year
'ETag': true,
},
dynamicContent: {
'Cache-Control': 'public, max-age=300, must-revalidate', // 5 minutes
'ETag': true,
},
userSpecific: {
'Cache-Control': 'private, max-age=0, no-cache',
'ETag': false,
},
api: {
'Cache-Control': 'public, max-age=60, stale-while-revalidate=300',
'ETag': true,
},
};
// Webpack configuration for cache busting
module.exports = {
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
publicPath: getCDNUrl('staticAssets', ''),
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
filename: 'vendor.[contenthash].js',
chunks: 'all',
},
},
},
},
};
Effective caching is essential for frontend performance.
// Service Worker for Advanced Caching
class CacheManager {
constructor() {
this.CACHE_NAME = 'app-cache-v1';
this.API_CACHE = 'api-cache-v1';
this.IMAGE_CACHE = 'image-cache-v1';
}
async install() {
const cache = await caches.open(this.CACHE_NAME);
const staticAssets = [
'/',
'/static/css/main.css',
'/static/js/main.js',
'/manifest.json',
];
return cache.addAll(staticAssets);
}
async fetch(request) {
const url = new URL(request.url);
// Cache strategy for different resource types
if (this.isStaticAsset(url)) {
return this.cacheFirst(request);
}
if (this.isAPIRequest(url)) {
return this.networkFirst(request);
}
if (this.isImageRequest(url)) {
return this.staleWhileRevalidate(request);
}
return fetch(request);
}
async cacheFirst(request) {
const cache = await caches.open(this.CACHE_NAME);
const cachedResponse = await cache.match(request);
if (cachedResponse) {
return cachedResponse;
}
const networkResponse = await fetch(request);
cache.put(request, networkResponse.clone());
return networkResponse;
}
async networkFirst(request) {
const cache = await caches.open(this.API_CACHE);
try {
const networkResponse = await fetch(request);
cache.put(request, networkResponse.clone());
return networkResponse;
} catch (error) {
const cachedResponse = await cache.match(request);
return cachedResponse || new Response('Offline', { status: 503 });
}
}
async staleWhileRevalidate(request) {
const cache = await caches.open(this.IMAGE_CACHE);
const cachedResponse = await cache.match(request);
const fetchPromise = fetch(request).then(networkResponse => {
cache.put(request, networkResponse.clone());
return networkResponse;
});
return cachedResponse || fetchPromise;
}
isStaticAsset(url) {
return /\.(js|css|woff2?|png|jpg|svg)$/.test(url.pathname);
}
isAPIRequest(url) {
return url.pathname.startsWith('/api/');
}
isImageRequest(url) {
return /\.(png|jpg|jpeg|webp|gif|svg)$/.test(url.pathname);
}
}
// Register service worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js');
});
}
// React Query for Server State Caching
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 10 * 60 * 1000, // 10 minutes
retry: 3,
refetchOnWindowFocus: false,
},
},
});
// Cache with different strategies
const useCachedData = (key, fetcher, options = {}) => {
return useQuery(key, fetcher, {
staleTime: options.staleTime || 5 * 60 * 1000,
cacheTime: options.cacheTime || 10 * 60 * 1000,
...options,
});
};
// Memory cache for computed values
class MemoryCache {
constructor(maxSize = 100) {
this.cache = new Map();
this.maxSize = maxSize;
}
get(key) {
if (this.cache.has(key)) {
const value = this.cache.get(key);
// Move to end (LRU)
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
return null;
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.maxSize) {
// Remove oldest entry
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
clear() {
this.cache.clear();
}
}
const memoCache = new MemoryCache(50);
export const memoize = (fn, keyGenerator = (...args) => JSON.stringify(args)) => {
return (...args) => {
const key = keyGenerator(...args);
const cached = memoCache.get(key);
if (cached !== null) {
return cached;
}
const result = fn(...args);
memoCache.set(key, result);
return result;
};
};
Micro-frontends enable teams to develop and deploy frontend applications independently.
// webpack.config.js for Host Application
const ModuleFederationPlugin = require('@module-federation/webpack');
module.exports = {
mode: 'development',
devServer: {
port: 3000,
},
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
userManagement: 'userManagement@http://localhost:3001/remoteEntry.js',
dashboard: 'dashboard@http://localhost:3002/remoteEntry.js',
analytics: 'analytics@http://localhost:3003/remoteEntry.js',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
'react-router-dom': { singleton: true },
},
}),
],
};
// webpack.config.js for Micro-frontend
module.exports = {
mode: 'development',
devServer: {
port: 3001,
},
plugins: [
new ModuleFederationPlugin({
name: 'userManagement',
filename: 'remoteEntry.js',
exposes: {
'./UserManagement': './src/UserManagement',
'./UserProfile': './src/UserProfile',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
};
// Event Bus for Cross-Micro-Frontend Communication
class EventBus {
constructor() {
this.events = {};
}
subscribe(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
// Return unsubscribe function
return () => {
this.events[event] = this.events[event].filter(cb => cb !== callback);
};
}
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(callback => callback(data));
}
}
clear() {
this.events = {};
}
}
// Global event bus instance
window.eventBus = window.eventBus || new EventBus();
// React Hook for Event Bus
import { useEffect, useCallback } from 'react';
export const useEventBus = (event, handler) => {
useEffect(() => {
if (!event || !handler) return;
const unsubscribe = window.eventBus.subscribe(event, handler);
return unsubscribe;
}, [event, handler]);
const emit = useCallback((eventName, data) => {
window.eventBus.emit(eventName, data);
}, []);
return { emit };
};
// Usage in components
const UserProfile = () => {
const { emit } = useEventBus('user-updated', (userData) => {
console.log('User updated:', userData);
});
const handleUserUpdate = (user) => {
emit('user-updated', user);
};
return (
<div>
{/* Component content */}
</div>
);
};
// Shared Store for Micro-Frontends
class SharedStore {
constructor() {
this.store = {};
this.subscribers = {};
}
getState(key) {
return this.store[key];
}
setState(key, value) {
this.store[key] = value;
this.notifySubscribers(key, value);
}
subscribe(key, callback) {
if (!this.subscribers[key]) {
this.subscribers[key] = [];
}
this.subscribers[key].push(callback);
return () => {
this.subscribers[key] = this.subscribers[key].filter(cb => cb !== callback);
};
}
notifySubscribers(key, value) {
if (this.subscribers[key]) {
this.subscribers[key].forEach(callback => callback(value));
}
}
}
window.sharedStore = window.sharedStore || new SharedStore();
// React Hook for Shared Store
export const useSharedState = (key, initialValue) => {
const [value, setValue] = useState(() => {
const stored = window.sharedStore.getState(key);
return stored !== undefined ? stored : initialValue;
});
useEffect(() => {
const unsubscribe = window.sharedStore.subscribe(key, setValue);
return unsubscribe;
}, [key]);
const setSharedValue = useCallback((newValue) => {
window.sharedStore.setState(key, newValue);
}, [key]);
return [value, setSharedValue];
};
Comprehensive performance monitoring is crucial for maintaining user experience.
// Web Vitals Monitoring
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
class PerformanceMonitor {
constructor(options = {}) {
this.apiEndpoint = options.apiEndpoint || '/api/metrics';
this.sampling = options.sampling || 0.1; // 10% sampling
this.bufferSize = options.bufferSize || 10;
this.buffer = [];
this.init();
}
init() {
if (Math.random() > this.sampling) return;
// Core Web Vitals
getCLS(this.handleMetric.bind(this));
getFID(this.handleMetric.bind(this));
getFCP(this.handleMetric.bind(this));
getLCP(this.handleMetric.bind(this));
getTTFB(this.handleMetric.bind(this));
// Custom metrics
this.monitorReactPerformance();
this.monitorResourceTiming();
this.monitorJSErrors();
}
handleMetric(metric) {
const data = {
name: metric.name,
value: metric.value,
rating: metric.rating,
delta: metric.delta,
id: metric.id,
url: window.location.href,
timestamp: Date.now(),
userAgent: navigator.userAgent,
connection: navigator.connection?.effectiveType,
};
this.buffer.push(data);
if (this.buffer.length >= this.bufferSize) {
this.flush();
}
}
monitorReactPerformance() {
// React DevTools Profiler API
if (window.React && window.React.Profiler) {
const originalProfiler = window.React.Profiler;
window.React.Profiler = (props) => {
return originalProfiler({
...props,
onRender: (id, phase, actualDuration, baseDuration, startTime, commitTime) => {
if (props.onRender) {
props.onRender(id, phase, actualDuration, baseDuration, startTime, commitTime);
}
this.handleMetric({
name: 'react-render',
value: actualDuration,
id: id,
extra: { phase, baseDuration, startTime, commitTime },
});
},
});
};
}
}
monitorResourceTiming() {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.entryType === 'resource') {
this.handleMetric({
name: 'resource-timing',
value: entry.duration,
id: entry.name,
extra: {
size: entry.transferSize,
type: entry.initiatorType,
cached: entry.transferSize === 0,
},
});
}
});
});
observer.observe({ entryTypes: ['resource'] });
}
monitorJSErrors() {
window.addEventListener('error', (event) => {
this.handleMetric({
name: 'js-error',
value: 1,
id: event.filename,
extra: {
message: event.message,
lineno: event.lineno,
colno: event.colno,
stack: event.error?.stack,
},
});
});
window.addEventListener('unhandledrejection', (event) => {
this.handleMetric({
name: 'unhandled-promise-rejection',
value: 1,
id: 'promise-rejection',
extra: {
reason: event.reason,
},
});
});
}
flush() {
if (this.buffer.length === 0) return;
const data = [...this.buffer];
this.buffer = [];
// Send to analytics service
fetch(this.apiEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).catch(console.error);
}
}
// Initialize performance monitoring
const performanceMonitor = new PerformanceMonitor({
apiEndpoint: '/api/metrics',
sampling: process.env.NODE_ENV === 'production' ? 0.1 : 1,
});
// React Performance Monitor Component
import React, { Profiler, useState, useEffect } from 'react';
const PerformanceProfiler = ({ id, children, onRender }) => {
const [renderMetrics, setRenderMetrics] = useState([]);
const handleRender = (id, phase, actualDuration, baseDuration, startTime, commitTime) => {
const metric = {
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
timestamp: Date.now(),
};
setRenderMetrics(prev => [...prev.slice(-19), metric]);
if (onRender) {
onRender(metric);
}
// Log slow renders
if (actualDuration > 16) { // Slower than 60fps
console.warn(`Slow render detected in ${id}:`, metric);
}
};
return (
<Profiler id={id} onRender={handleRender}>
{children}
</Profiler>
);
};
// Usage
const App = () => {
return (
<PerformanceProfiler id="App">
<Header />
<PerformanceProfiler id="MainContent">
<MainContent />
</PerformanceProfiler>
<Footer />
</PerformanceProfiler>
);
};
Design components that can scale with your application.
// Compound Component Pattern
const Tabs = ({ children, defaultTab = 0 }) => {
const [activeTab, setActiveTab] = useState(defaultTab);
return (
<div className="tabs">
{React.Children.map(children, (child, index) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, {
isActive: index === activeTab,
onActivate: () => setActiveTab(index),
index,
});
}
return child;
})}
</div>
);
};
const TabList = ({ children }) => (
<div className="tab-list" role="tablist">
{children}
</div>
);
const Tab = ({ children, isActive, onActivate, index }) => (
<button
className={`tab ${isActive ? 'tab--active' : ''}`}
onClick={onActivate}
role="tab"
aria-selected={isActive}
id={`tab-${index}`}
aria-controls={`panel-${index}`}
>
{children}
</button>
);
const TabPanels = ({ children }) => (
<div className="tab-panels">
{children}
</div>
);
const TabPanel = ({ children, isActive, index }) => (
<div
className={`tab-panel ${isActive ? 'tab-panel--active' : ''}`}
role="tabpanel"
id={`panel-${index}`}
aria-labelledby={`tab-${index}`}
hidden={!isActive}
>
{children}
</div>
);
// Attach components as properties
Tabs.List = TabList;
Tabs.Tab = Tab;
Tabs.Panels = TabPanels;
Tabs.Panel = TabPanel;
// Usage
<Tabs defaultTab={0}>
<Tabs.List>
<Tabs.Tab>Tab 1</Tabs.Tab>
<Tabs.Tab>Tab 2</Tabs.Tab>
<Tabs.Tab>Tab 3</Tabs.Tab>
</Tabs.List>
<Tabs.Panels>
<Tabs.Panel>Content 1</Tabs.Panel>
<Tabs.Panel>Content 2</Tabs.Panel>
<Tabs.Panel>Content 3</Tabs.Panel>
</Tabs.Panels>
</Tabs>
// Design System Token Management
const designTokens = {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6',
900: '#1e3a8a',
},
semantic: {
success: '#10b981',
warning: '#f59e0b',
error: '#ef4444',
info: '#3b82f6',
},
},
spacing: {
xs: '0.25rem',
sm: '0.5rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem',
},
typography: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
mono: ['Monaco', 'monospace'],
},
fontSize: {
xs: ['0.75rem', '1rem'],
sm: ['0.875rem', '1.25rem'],
base: ['1rem', '1.5rem'],
lg: ['1.125rem', '1.75rem'],
},
},
breakpoints: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
},
};
// Theme Provider
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext(designTokens);
export const ThemeProvider = ({ theme = designTokens, children }) => (
<ThemeContext.Provider value={theme}>
{children}
</ThemeContext.Provider>
);
export const useTheme = () => {
const theme = useContext(ThemeContext);
if (!theme) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return theme;
};
// Styled Component Factory
export const styled = (component) => (styles) => {
return React.forwardRef((props, ref) => {
const theme = useTheme();
const computedStyles = typeof styles === 'function'
? styles({ theme, ...props })
: styles;
return React.createElement(component, {
...props,
ref,
style: { ...computedStyles, ...props.style },
});
});
};
// Usage
const StyledButton = styled('button')(({ theme, variant = 'primary' }) => ({
padding: `${theme.spacing.sm} ${theme.spacing.md}`,
backgroundColor: theme.colors.primary[500],
color: 'white',
border: 'none',
borderRadius: '0.375rem',
fontFamily: theme.typography.fontFamily.sans.join(', '),
fontSize: theme.typography.fontSize.sm[0],
lineHeight: theme.typography.fontSize.sm[1],
cursor: 'pointer',
transition: 'all 0.2s ease',
'&:hover': {
backgroundColor: theme.colors.primary[600],
},
'&:disabled': {
opacity: 0.5,
cursor: 'not-allowed',
},
}));
Managing state in large applications requires careful architecture.
// Redux Toolkit with Feature Slices
import { configureStore, createSlice } from '@reduxjs/toolkit';
// User slice
const userSlice = createSlice({
name: 'user',
initialState: {
profile: null,
preferences: {},
loading: false,
error: null,
},
reducers: {
setUser: (state, action) => {
state.profile = action.payload;
},
updatePreferences: (state, action) => {
state.preferences = { ...state.preferences, ...action.payload };
},
setLoading: (state, action) => {
state.loading = action.payload;
},
setError: (state, action) => {
state.error = action.payload;
},
},
});
// Notification slice
const notificationSlice = createSlice({
name: 'notifications',
initialState: {
items: [],
unreadCount: 0,
},
reducers: {
addNotification: (state, action) => {
state.items.push(action.payload);
state.unreadCount += 1;
},
removeNotification: (state, action) => {
state.items = state.items.filter(item => item.id !== action.payload);
},
markAsRead: (state, action) => {
const notification = state.items.find(item => item.id === action.payload);
if (notification && !notification.read) {
notification.read = true;
state.unreadCount = Math.max(0, state.unreadCount - 1);
}
},
},
});
// Store configuration
const store = configureStore({
reducer: {
user: userSlice.reducer,
notifications: notificationSlice.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: ['persist/PERSIST'],
},
}),
});
// Feature-based hooks
export const useUser = () => {
const user = useSelector(state => state.user);
const dispatch = useDispatch();
return {
...user,
setUser: (userData) => dispatch(userSlice.actions.setUser(userData)),
updatePreferences: (prefs) => dispatch(userSlice.actions.updatePreferences(prefs)),
setLoading: (loading) => dispatch(userSlice.actions.setLoading(loading)),
setError: (error) => dispatch(userSlice.actions.setError(error)),
};
};
export const useNotifications = () => {
const notifications = useSelector(state => state.notifications);
const dispatch = useDispatch();
return {
...notifications,
addNotification: (notification) =>
dispatch(notificationSlice.actions.addNotification(notification)),
removeNotification: (id) =>
dispatch(notificationSlice.actions.removeNotification(id)),
markAsRead: (id) =>
dispatch(notificationSlice.actions.markAsRead(id)),
};
};
Frontend security is crucial for protecting user data and preventing attacks.
// CSP Configuration
const CSP_DIRECTIVES = {
'default-src': ["'self'"],
'script-src': [
"'self'",
"'unsafe-inline'", // Only for development
'https://cdn.jsdelivr.net',
'https://unpkg.com',
],
'style-src': [
"'self'",
"'unsafe-inline'",
'https://fonts.googleapis.com',
],
'img-src': [
"'self'",
'data:',
'https:',
],
'font-src': [
"'self'",
'https://fonts.gstatic.com',
],
'connect-src': [
"'self'",
'https://api.example.com',
'wss://websocket.example.com',
],
'frame-ancestors': ["'none'"],
'base-uri': ["'self'"],
'form-action': ["'self'"],
};
const generateCSP = (directives) => {
return Object.entries(directives)
.map(([directive, sources]) => `${directive} ${sources.join(' ')}`)
.join('; ');
};
// Set CSP header
const cspHeader = generateCSP(CSP_DIRECTIVES);
document.querySelector('meta[http-equiv="Content-Security-Policy"]')
?.setAttribute('content', cspHeader);
// XSS Prevention
class SecurityUtils {
static sanitizeHTML(html) {
const div = document.createElement('div');
div.textContent = html;
return div.innerHTML;
}
static sanitizeURL(url) {
try {
const parsedUrl = new URL(url);
const allowedProtocols = ['http:', 'https:', 'mailto:', 'tel:'];
if (!allowedProtocols.includes(parsedUrl.protocol)) {
return '#';
}
return parsedUrl.href;
} catch {
return '#';
}
}
static validateInput(input, type) {
const validators = {
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
phone: /^\+?[\d\s\-\(\)]{10,}$/,
url: /^https?:\/\/.+$/,
alphanumeric: /^[a-zA-Z0-9]+$/,
};
return validators[type]?.test(input) || false;
}
static encodeForHTML(str) {
const div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
}
// Safe component for rendering user content
const SafeHTML = ({ content, allowedTags = [] }) => {
const sanitized = useMemo(() => {
// Use a library like DOMPurify for production
return SecurityUtils.sanitizeHTML(content);
}, [content]);
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
};
Comprehensive monitoring ensures system reliability and performance.
// Error Tracking Service
class ErrorTracker {
constructor(config) {
this.apiEndpoint = config.apiEndpoint;
this.environment = config.environment;
this.userId = config.userId;
this.sessionId = this.generateSessionId();
this.setupGlobalErrorHandlers();
}
setupGlobalErrorHandlers() {
window.addEventListener('error', this.handleError.bind(this));
window.addEventListener('unhandledrejection', this.handlePromiseRejection.bind(this));
}
handleError(event) {
this.track({
type: 'javascript-error',
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error?.stack,
url: window.location.href,
userAgent: navigator.userAgent,
timestamp: new Date().toISOString(),
});
}
handlePromiseRejection(event) {
this.track({
type: 'promise-rejection',
reason: event.reason,
url: window.location.href,
timestamp: new Date().toISOString(),
});
}
track(error) {
const errorData = {
...error,
environment: this.environment,
userId: this.userId,
sessionId: this.sessionId,
breadcrumbs: this.getBreadcrumbs(),
context: this.getContext(),
};
// Send to error tracking service
fetch(this.apiEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(errorData),
}).catch(console.error);
}
getBreadcrumbs() {
// Return array of user actions leading to error
return window.breadcrumbs || [];
}
getContext() {
return {
viewport: {
width: window.innerWidth,
height: window.innerHeight,
},
connection: navigator.connection?.effectiveType,
memory: navigator.deviceMemory,
cores: navigator.hardwareConcurrency,
};
}
generateSessionId() {
return 'session_' + Math.random().toString(36).substr(2, 9);
}
}
// Initialize error tracking
const errorTracker = new ErrorTracker({
apiEndpoint: '/api/errors',
environment: process.env.NODE_ENV,
userId: getCurrentUserId(),
});
// Real-time Performance Dashboard
import React, { useState, useEffect } from 'react';
const PerformanceDashboard = () => {
const [metrics, setMetrics] = useState({
lcp: null,
fid: null,
cls: null,
ttfb: null,
bundleSize: null,
errorRate: null,
});
useEffect(() => {
const fetchMetrics = async () => {
try {
const response = await fetch('/api/metrics/dashboard');
const data = await response.json();
setMetrics(data);
} catch (error) {
console.error('Failed to fetch metrics:', error);
}
};
fetchMetrics();
const interval = setInterval(fetchMetrics, 30000); // Update every 30s
return () => clearInterval(interval);
}, []);
const getScoreColor = (value, thresholds) => {
if (value <= thresholds.good) return '#10b981';
if (value <= thresholds.fair) return '#f59e0b';
return '#ef4444';
};
return (
<div className="performance-dashboard">
<h2>Performance Metrics</h2>
<div className="metrics-grid">
<MetricCard
title="Largest Contentful Paint"
value={metrics.lcp}
unit="ms"
thresholds={{ good: 2500, fair: 4000 }}
description="Time to render the largest content element"
/>
<MetricCard
title="First Input Delay"
value={metrics.fid}
unit="ms"
thresholds={{ good: 100, fair: 300 }}
description="Time from first user interaction to browser response"
/>
<MetricCard
title="Cumulative Layout Shift"
value={metrics.cls}
unit=""
thresholds={{ good: 0.1, fair: 0.25 }}
description="Amount of unexpected layout shift"
/>
<MetricCard
title="Time to First Byte"
value={metrics.ttfb}
unit="ms"
thresholds={{ good: 800, fair: 1800 }}
description="Time from navigation to first byte received"
/>
</div>
</div>
);
};
const MetricCard = ({ title, value, unit, thresholds, description }) => {
const color = value ? getScoreColor(value, thresholds) : '#6b7280';
return (
<div className="metric-card">
<h3>{title}</h3>
<div className="metric-value" style={{ color }}>
{value ? `${value}${unit}` : 'Loading...'}
</div>
<p className="metric-description">{description}</p>
</div>
);
};
Frontend system design requires a holistic approach that considers performance, scalability, maintainability, and user experience. Key takeaways:
By following these principles and implementing these patterns, you can build frontend systems that scale effectively while maintaining excellent user experience and developer productivity.