Skip to main content

Performance Monitoring

Performance monitoring is crucial for understanding how your Refract application behaves in the real world. It helps you identify bottlenecks, track user experience, and make data-driven decisions about optimizations.

Think of it as having a health monitor for your application - it tells you when things are running smoothly and alerts you when performance degrades, so you can fix issues before they affect your users.

Why Monitor Performance?​

Your application might work perfectly on your development machine, but real users have different devices, network conditions, and usage patterns. Performance monitoring helps you:

  • Catch issues early - Identify problems before they impact many users
  • Understand user experience - See how your app performs on different devices
  • Make informed decisions - Use real data to guide optimization efforts
  • Track improvements - Measure the impact of performance optimizations

Without monitoring, you're flying blind - you won't know if your application is fast or slow for real users.

Built-in Performance Tracking​

Refract provides built-in performance monitoring through the application configuration:

Enable Performance Monitoring
import { createApp } from 'refract';

const app = createApp(App, {
// Enable performance monitoring
performance: {
enabled: true,
sampleRate: 0.1, // Monitor 10% of interactions
thresholds: {
componentRender: 16, // Warn if component takes >16ms to render
effectExecution: 5, // Warn if effect takes >5ms
stateUpdate: 1 // Warn if state update takes >1ms
}
},

// Performance event handler
onPerformanceEntry: (entry) => {
// Send to your analytics service
analytics.track('performance', {
type: entry.type,
duration: entry.duration,
componentName: entry.componentName,
timestamp: entry.timestamp
});
}
});

Core Web Vitals Integration​

Track Core Web Vitals to understand user experience:

Core Web Vitals Tracking
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

const trackWebVitals = (metric) => {
// Send to your analytics service
analytics.track('web-vital', {
name: metric.name,
value: metric.value,
id: metric.id,
delta: metric.delta
});
};

// Track all Core Web Vitals
getCLS(trackWebVitals);
getFID(trackWebVitals);
getFCP(trackWebVitals);
getLCP(trackWebVitals);
getTTFB(trackWebVitals);

// Initialize Refract app
const app = createApp(App, {
onMount: () => {
// Track app initialization time
performance.mark('refract-app-ready');
performance.measure('refract-init', 'navigationStart', 'refract-app-ready');
}
});

Memory Usage Monitoring​

Track memory usage to prevent memory leaks:

Memory Usage Tracker
const useMemoryMonitor = (lens) => {
const memoryInfo = lens.useRefraction(null);

lens.useEffect(() => {
const updateMemoryInfo = () => {
if ('memory' in performance) {
memoryInfo.set({
usedJSHeapSize: performance.memory.usedJSHeapSize,
totalJSHeapSize: performance.memory.totalJSHeapSize,
jsHeapSizeLimit: performance.memory.jsHeapSizeLimit,
timestamp: Date.now()
});
}
};

// Update immediately
updateMemoryInfo();

// Update every 30 seconds
const interval = setInterval(updateMemoryInfo, 30000);

return () => clearInterval(interval);
}, []);

return memoryInfo.value;
};

const MemoryMonitor = createComponent(({ lens }) => {
const memory = useMemoryMonitor(lens);

lens.useEffect(() => {
if (!memory) return;

const usagePercent = (memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100;

// Alert if memory usage is high
if (usagePercent > 80) {
analytics.track('high-memory-usage', {
usagePercent,
usedMB: Math.round(memory.usedJSHeapSize / 1024 / 1024),
totalMB: Math.round(memory.totalJSHeapSize / 1024 / 1024)
});
}
}, [memory]);

if (!memory) return <div>Memory info not available</div>;

const usedMB = Math.round(memory.usedJSHeapSize / 1024 / 1024);
const totalMB = Math.round(memory.totalJSHeapSize / 1024 / 1024);
const limitMB = Math.round(memory.jsHeapSizeLimit / 1024 / 1024);

return (
<div className="memory-monitor">
<h3>Memory Usage</h3>
<p>Used: {usedMB}MB / {totalMB}MB</p>
<p>Limit: {limitMB}MB</p>
<div
className="memory-bar"
style={{
width: '200px',
height: '20px',
backgroundColor: '#f0f0f0',
borderRadius: '10px',
overflow: 'hidden'
}}
>
<div
style={{
width: `${(usedMB / limitMB) * 100}%`,
height: '100%',
backgroundColor: usedMB / limitMB > 0.8 ? '#ff4444' : '#44ff44',
transition: 'width 0.3s ease'
}}
/>
</div>
</div>
);
});

User Interaction Tracking​

Monitor user interactions and their performance impact:

Interaction Performance Tracker
const useInteractionTracking = (lens) => {
lens.useEffect(() => {
const trackInteraction = (event) => {
const startTime = performance.now();

// Use requestIdleCallback to measure after interaction is processed
requestIdleCallback(() => {
const endTime = performance.now();
const duration = endTime - startTime;

analytics.track('user-interaction', {
type: event.type,
target: event.target.tagName,
duration,
timestamp: startTime
});

// Track slow interactions
if (duration > 100) {
analytics.track('slow-interaction', {
type: event.type,
duration,
target: event.target.tagName
});
}
});
};

// Track various interaction types
const events = ['click', 'input', 'scroll', 'keydown'];
events.forEach(eventType => {
document.addEventListener(eventType, trackInteraction, { passive: true });
});

return () => {
events.forEach(eventType => {
document.removeEventListener(eventType, trackInteraction);
});
};
}, []);
};

const App = createComponent(({ lens }) => {
useInteractionTracking(lens);

return (
<div>
{/* Your app content */}
</div>
);
});

Bundle Size Monitoring​

Track bundle size and loading performance:

Bundle Size Tracker
// Add to your build process
const bundleAnalyzer = {
onBuildComplete: (stats) => {
const bundleSize = stats.assets
.filter(asset => asset.name.endsWith('.js'))
.reduce((total, asset) => total + asset.size, 0);

// Track bundle size changes
analytics.track('bundle-size', {
size: bundleSize,
gzipSize: stats.gzipSize,
chunks: stats.chunks.length,
buildTime: stats.time
});

// Alert if bundle size increases significantly
if (bundleSize > 500000) { // 500KB threshold
console.warn(`Bundle size is large: ${Math.round(bundleSize / 1024)}KB`);
}
}
};

// Runtime bundle tracking
const trackResourceTiming = () => {
const resources = performance.getEntriesByType('resource');

resources
.filter(resource => resource.name.includes('.js'))
.forEach(resource => {
analytics.track('resource-timing', {
name: resource.name,
size: resource.transferSize,
loadTime: resource.responseEnd - resource.requestStart,
cacheHit: resource.transferSize === 0
});
});
};

// Call after app loads
window.addEventListener('load', trackResourceTiming);

Error Boundary with Performance Impact​

Track how errors affect performance:

Performance-Aware Error Boundary
const ErrorBoundary = createComponent(({ lens, children }) => {
const error = lens.useRefraction(null);
const errorCount = lens.useRefraction(0);
const performanceImpact = lens.useRefraction(null);

lens.useEffect(() => {
const handleError = (event) => {
const errorTime = performance.now();

error.set({
message: event.error?.message || 'Unknown error',
stack: event.error?.stack,
timestamp: errorTime
});

errorCount.set(count => count + 1);

// Measure performance impact
requestIdleCallback(() => {
const recoveryTime = performance.now() - errorTime;
performanceImpact.set(recoveryTime);

analytics.track('error-performance-impact', {
errorMessage: event.error?.message,
recoveryTime,
errorCount: errorCount.value
});
});
};

window.addEventListener('error', handleError);
window.addEventListener('unhandledrejection', handleError);

return () => {
window.removeEventListener('error', handleError);
window.removeEventListener('unhandledrejection', handleError);
};
}, []);

if (error.value) {
return (
<div className="error-boundary">
<h2>Something went wrong</h2>
<p>{error.value.message}</p>
{performanceImpact.value && (
<p>Recovery time: {performanceImpact.value.toFixed(2)}ms</p>
)}
<button onClick={() => {
error.set(null);
performanceImpact.set(null);
}}>
Try Again
</button>
</div>
);
}

return children;
});

Performance Dashboard​

Create a real-time performance dashboard:

Performance Dashboard
const PerformanceDashboard = createComponent(({ lens }) => {
const metrics = lens.useRefraction({
renderTime: 0,
memoryUsage: 0,
errorCount: 0,
interactionLatency: 0
});

lens.useEffect(() => {
const updateMetrics = () => {
// Collect current metrics
const currentMetrics = {
renderTime: performance.now() % 100, // Simulated
memoryUsage: performance.memory?.usedJSHeapSize || 0,
errorCount: window.errorCount || 0,
interactionLatency: window.lastInteractionTime || 0
};

metrics.set(currentMetrics);
};

const interval = setInterval(updateMetrics, 1000);
return () => clearInterval(interval);
}, []);

return (
<div className="performance-dashboard">
<h2>Performance Dashboard</h2>

<div className="metrics-grid">
<div className="metric">
<h3>Render Time</h3>
<span className={metrics.value.renderTime > 16 ? 'warning' : 'good'}>
{metrics.value.renderTime.toFixed(2)}ms
</span>
</div>

<div className="metric">
<h3>Memory Usage</h3>
<span>
{Math.round(metrics.value.memoryUsage / 1024 / 1024)}MB
</span>
</div>

<div className="metric">
<h3>Error Count</h3>
<span className={metrics.value.errorCount > 0 ? 'error' : 'good'}>
{metrics.value.errorCount}
</span>
</div>

<div className="metric">
<h3>Interaction Latency</h3>
<span className={metrics.value.interactionLatency > 100 ? 'warning' : 'good'}>
{metrics.value.interactionLatency.toFixed(2)}ms
</span>
</div>
</div>
</div>
);
});

Best Practices​

  • Monitor continuously: Set up automated performance monitoring in production
  • Set meaningful thresholds: Define performance budgets for your application
  • Track user impact: Focus on metrics that affect user experience
  • Use sampling: Don't track every interaction to avoid performance overhead
  • Correlate with business metrics: Connect performance data to user behavior
  • Set up alerts: Get notified when performance degrades
  • Regular audits: Periodically review and optimize based on monitoring data