15 min read
CRO Complete Guide
Master conversion rate optimization with data-driven strategies, statistical testing, and proven frameworks used by top-performing websites.
What is Conversion Rate Optimization?
Conversion Rate Optimization (CRO) is the systematic process of increasing the percentage of website visitors who take a desired action—whether that's making a purchase, signing up for a newsletter, or requesting a demo.
code
Conversion Rate = (Conversions / Total Visitors) × 100
Example:
─────────────────────────────────────────────────────
Visitors: 10,000
Conversions: 250
─────────────────────────────────────────────────────
Rate: 2.5%
With CRO optimization:
─────────────────────────────────────────────────────
Visitors: 10,000
Conversions: 375
─────────────────────────────────────────────────────
Rate: 3.75% (+50% improvement)The Power of CRO: A 1% improvement in conversion rate can translate to millions in additional revenue without increasing ad spend.
The CRO Framework
1. Research Phase
Before making any changes, understand your current state:
code
interface CROAuditFramework {
quantitative: {
analytics: 'GA4 funnel analysis';
heatmaps: 'Click and scroll tracking';
sessionRecordings: 'User journey replay';
funnelAnalysis: 'Drop-off identification';
};
qualitative: {
surveys: 'On-site and email surveys';
userInterviews: 'Direct feedback sessions';
usabilityTests: 'Task completion studies';
customerSupport: 'Common questions/issues';
};
technical: {
pageSpeed: 'Core Web Vitals audit';
mobileUX: 'Responsive design review';
browserTesting: 'Cross-browser compatibility';
accessibility: 'WCAG compliance check';
};
}2. Hypothesis Development
Every CRO test starts with a hypothesis:
code
## Hypothesis Template
**Based on:** [Research insight/data]
**We believe that:** [Change description]
**Will cause:** [Expected outcome]
**We'll measure this by:** [Key metric]
## Example Hypothesis
**Based on:** Session recordings showing 68% of users scroll past the CTA button
**We believe that:** Moving the primary CTA above the fold
**Will cause:** Increase in demo request submissions
**We'll measure this by:** Demo form completion rate (target: +15%)3. Prioritization with ICE/PIE Framework
code
interface TestPrioritization {
// ICE Framework
ice: {
impact: number; // 1-10: Potential impact on conversion
confidence: number; // 1-10: How sure are we this will work?
ease: number; // 1-10: How easy is implementation?
score: number; // Average of all three
};
// PIE Framework
pie: {
potential: number; // 1-10: How much can this page improve?
importance: number; // 1-10: How valuable is traffic to this page?
ease: number; // 1-10: How easy is the test to run?
score: number;
};
}
function calculateICE(impact: number, confidence: number, ease: number): number {
return (impact + confidence + ease) / 3;
}
function calculatePIE(potential: number, importance: number, ease: number): number {
return (potential + importance + ease) / 3;
}
// Example prioritization
const tests = [
{ name: 'New headline copy', ice: calculateICE(8, 7, 9) }, // 8.0
{ name: 'Redesign checkout flow', ice: calculateICE(9, 6, 3) }, // 6.0
{ name: 'Add trust badges', ice: calculateICE(5, 8, 10) }, // 7.7
{ name: 'Video testimonials', ice: calculateICE(7, 5, 4) }, // 5.3
];
// Sort by ICE score
tests.sort((a, b) => b.ice - a.ice);Conversion Funnel Optimization
Understanding the Funnel
code
┌─────────────────────────┐
│ AWARENESS │ ← Traffic Sources
│ 100,000 visitors │
└───────────┬─────────────┘
│ 40% continue
┌───────────▼─────────────┐
│ INTEREST │ ← Landing Pages
│ 40,000 engaged │
└───────────┬─────────────┘
│ 25% continue
┌───────────▼─────────────┐
│ CONSIDERATION │ ← Product Pages
│ 10,000 considering │
└───────────┬─────────────┘
│ 30% continue
┌───────────▼─────────────┐
│ INTENT │ ← Cart/Signup
│ 3,000 intending │
└───────────┬─────────────┘
│ 50% continue
┌───────────▼─────────────┐
│ CONVERSION │ ← Purchase/Submit
│ 1,500 converted │
└─────────────────────────┘
Overall Conversion Rate: 1.5%Funnel Tracking Implementation
code
class ConversionFunnel {
constructor(funnelName) {
this.funnelName = funnelName;
this.steps = [];
this.sessionId = this.getOrCreateSessionId();
}
getOrCreateSessionId() {
let sessionId = sessionStorage.getItem('funnel_session_id');
if (!sessionId) {
sessionId = 'sess_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
sessionStorage.setItem('funnel_session_id', sessionId);
}
return sessionId;
}
trackStep(stepName, metadata = {}) {
const stepData = {
funnel: this.funnelName,
step: stepName,
stepIndex: this.steps.length + 1,
sessionId: this.sessionId,
timestamp: Date.now(),
url: window.location.href,
referrer: document.referrer,
...metadata
};
this.steps.push(stepData);
// Send to analytics
this.sendToAnalytics(stepData);
// GA4 custom event
if (typeof gtag !== 'undefined') {
gtag('event', 'funnel_step', {
funnel_name: this.funnelName,
step_name: stepName,
step_index: stepData.stepIndex
});
}
}
trackConversion(value, metadata = {}) {
const conversionData = {
funnel: this.funnelName,
type: 'conversion',
sessionId: this.sessionId,
value: value,
stepsCompleted: this.steps.length,
timeToConvert: Date.now() - this.steps[0]?.timestamp,
...metadata
};
this.sendToAnalytics(conversionData);
}
trackDropoff(reason = 'unknown') {
const dropoffData = {
funnel: this.funnelName,
type: 'dropoff',
sessionId: this.sessionId,
lastStep: this.steps[this.steps.length - 1]?.step,
stepsCompleted: this.steps.length,
reason: reason,
timestamp: Date.now()
};
this.sendToAnalytics(dropoffData);
}
async sendToAnalytics(data) {
try {
await fetch('/api/analytics/funnel', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
} catch (error) {
console.error('Funnel tracking error:', error);
}
}
}
// Usage example: E-commerce checkout funnel
const checkoutFunnel = new ConversionFunnel('ecommerce_checkout');
// Track each step
checkoutFunnel.trackStep('cart_view', { itemCount: 3, cartValue: 149.99 });
checkoutFunnel.trackStep('checkout_start');
checkoutFunnel.trackStep('shipping_info');
checkoutFunnel.trackStep('payment_info');
checkoutFunnel.trackStep('order_review');
checkoutFunnel.trackConversion(149.99, { orderId: 'ORD-12345' });Statistical Significance in CRO
Understanding Sample Size Requirements
code
interface SampleSizeCalculation {
baselineConversionRate: number; // Current conversion rate
minimumDetectableEffect: number; // Smallest improvement worth detecting
statisticalPower: number; // Usually 0.80 (80%)
significanceLevel: number; // Usually 0.05 (95% confidence)
}
function calculateSampleSize({
baselineConversionRate,
minimumDetectableEffect,
statisticalPower = 0.80,
significanceLevel = 0.05
}: SampleSizeCalculation): number {
const p1 = baselineConversionRate;
const p2 = baselineConversionRate * (1 + minimumDetectableEffect);
// Z-scores for significance and power
const zAlpha = 1.96; // 95% confidence
const zBeta = 0.84; // 80% power
const pooledP = (p1 + p2) / 2;
const effect = Math.abs(p2 - p1);
const numerator = 2 * pooledP * (1 - pooledP) * Math.pow(zAlpha + zBeta, 2);
const denominator = Math.pow(effect, 2);
return Math.ceil(numerator / denominator);
}
// Example: 3% baseline, detect 10% relative improvement
const sampleSize = calculateSampleSize({
baselineConversionRate: 0.03,
minimumDetectableEffect: 0.10,
statisticalPower: 0.80,
significanceLevel: 0.05
});
console.log(`Required sample size per variant: ${sampleSize}`);
// Output: ~25,000 visitors per variantCalculating Statistical Significance
code
function calculateSignificance(controlVisitors, controlConversions,
treatmentVisitors, treatmentConversions) {
const controlRate = controlConversions / controlVisitors;
const treatmentRate = treatmentConversions / treatmentVisitors;
// Pooled standard error
const pooledRate = (controlConversions + treatmentConversions) /
(controlVisitors + treatmentVisitors);
const standardError = Math.sqrt(
pooledRate * (1 - pooledRate) *
(1 / controlVisitors + 1 / treatmentVisitors)
);
// Z-score
const zScore = (treatmentRate - controlRate) / standardError;
// Two-tailed p-value (approximation)
const pValue = 2 * (1 - normalCDF(Math.abs(zScore)));
// Confidence interval
const confidenceInterval = {
lower: (treatmentRate - controlRate) - 1.96 * standardError,
upper: (treatmentRate - controlRate) + 1.96 * standardError
};
return {
controlRate: (controlRate * 100).toFixed(2) + '%',
treatmentRate: (treatmentRate * 100).toFixed(2) + '%',
relativeLift: (((treatmentRate - controlRate) / controlRate) * 100).toFixed(2) + '%',
zScore: zScore.toFixed(3),
pValue: pValue.toFixed(4),
isSignificant: pValue < 0.05,
confidenceInterval: {
lower: (confidenceInterval.lower * 100).toFixed(2) + '%',
upper: (confidenceInterval.upper * 100).toFixed(2) + '%'
}
};
}
// Normal CDF approximation
function normalCDF(x) {
const a1 = 0.254829592;
const a2 = -0.284496736;
const a3 = 1.421413741;
const a4 = -1.453152027;
const a5 = 1.061405429;
const p = 0.3275911;
const sign = x < 0 ? -1 : 1;
x = Math.abs(x) / Math.sqrt(2);
const t = 1.0 / (1.0 + p * x);
const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);
return 0.5 * (1.0 + sign * y);
}
// Example calculation
const results = calculateSignificance(
10000, 300, // Control: 10,000 visitors, 300 conversions (3%)
10000, 360 // Treatment: 10,000 visitors, 360 conversions (3.6%)
);
console.log(results);
// {
// controlRate: '3.00%',
// treatmentRate: '3.60%',
// relativeLift: '20.00%',
// zScore: '2.501',
// pValue: '0.0124',
// isSignificant: true,
// confidenceInterval: { lower: '0.13%', upper: '1.07%' }
// }Page Speed and Conversion
Core Web Vitals Impact
code
Speed Impact on Conversion:
Page Load Time │ Conversion Impact
───────────────────┼────────────────────
0-2 seconds │ Baseline (optimal)
2-3 seconds │ -7% conversions
3-4 seconds │ -16% conversions
4-5 seconds │ -25% conversions
5+ seconds │ -32% conversions
Mobile bounce rate increases 32% as load time
goes from 1s to 3s (Google data)Performance Monitoring for CRO
code
class CROPerformanceMonitor {
constructor() {
this.metrics = {};
this.init();
}
init() {
// Observe Core Web Vitals
this.observeLCP();
this.observeFID();
this.observeCLS();
this.observeTTFB();
}
observeLCP() {
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
this.metrics.lcp = lastEntry.renderTime || lastEntry.loadTime;
this.sendMetric('LCP', this.metrics.lcp);
}).observe({ type: 'largest-contentful-paint', buffered: true });
}
observeFID() {
new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach((entry) => {
this.metrics.fid = entry.processingStart - entry.startTime;
this.sendMetric('FID', this.metrics.fid);
});
}).observe({ type: 'first-input', buffered: true });
}
observeCLS() {
let clsValue = 0;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
}
this.metrics.cls = clsValue;
this.sendMetric('CLS', clsValue);
}).observe({ type: 'layout-shift', buffered: true });
}
observeTTFB() {
const navigationEntry = performance.getEntriesByType('navigation')[0];
if (navigationEntry) {
this.metrics.ttfb = navigationEntry.responseStart - navigationEntry.requestStart;
this.sendMetric('TTFB', this.metrics.ttfb);
}
}
sendMetric(name, value) {
// Correlate with conversion data
if (typeof gtag !== 'undefined') {
gtag('event', 'web_vital', {
metric_name: name,
metric_value: Math.round(value),
page_path: window.location.pathname
});
}
// Send to your analytics
fetch('/api/analytics/performance', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
metric: name,
value: value,
url: window.location.href,
timestamp: Date.now(),
deviceType: this.getDeviceType(),
connectionType: navigator.connection?.effectiveType
})
});
}
getDeviceType() {
const width = window.innerWidth;
if (width < 768) return 'mobile';
if (width < 1024) return 'tablet';
return 'desktop';
}
getReport() {
return {
lcp: { value: this.metrics.lcp, rating: this.rateLCP(this.metrics.lcp) },
fid: { value: this.metrics.fid, rating: this.rateFID(this.metrics.fid) },
cls: { value: this.metrics.cls, rating: this.rateCLS(this.metrics.cls) },
ttfb: { value: this.metrics.ttfb, rating: this.rateTTFB(this.metrics.ttfb) }
};
}
rateLCP(value) {
if (value <= 2500) return 'good';
if (value <= 4000) return 'needs-improvement';
return 'poor';
}
rateFID(value) {
if (value <= 100) return 'good';
if (value <= 300) return 'needs-improvement';
return 'poor';
}
rateCLS(value) {
if (value <= 0.1) return 'good';
if (value <= 0.25) return 'needs-improvement';
return 'poor';
}
rateTTFB(value) {
if (value <= 800) return 'good';
if (value <= 1800) return 'needs-improvement';
return 'poor';
}
}
const perfMonitor = new CROPerformanceMonitor();Psychological Principles for CRO
Cialdini's Principles Applied
code
interface PersuasionPrinciples {
reciprocity: {
principle: 'Give value first';
implementation: [
'Free tools and calculators',
'Valuable content downloads',
'Free trials without credit card'
];
};
commitment: {
principle: 'Start with small asks';
implementation: [
'Micro-commitments (email signup)',
'Progressive profiling',
'Step-by-step wizards'
];
};
socialProof: {
principle: 'Show others are doing it';
implementation: [
'Customer testimonials',
'User counts and statistics',
'Trust badges and certifications'
];
};
authority: {
principle: 'Demonstrate expertise';
implementation: [
'Industry certifications',
'Expert endorsements',
'Media mentions and logos'
];
};
liking: {
principle: 'Be relatable and friendly';
implementation: [
'Team photos and bios',
'Conversational copy',
'Brand personality'
];
};
scarcity: {
principle: 'Limited availability';
implementation: [
'Limited-time offers',
'Stock availability',
'Exclusive access'
];
};
}Implementing Social Proof
code
class SocialProofWidget {
constructor(container, options = {}) {
this.container = container;
this.options = {
showCount: true,
showRecentActivity: true,
showTestimonials: true,
refreshInterval: 30000,
...options
};
this.init();
}
async init() {
await this.fetchSocialProofData();
this.render();
if (this.options.showRecentActivity) {
this.startRealtimeNotifications();
}
}
async fetchSocialProofData() {
const response = await fetch('/api/social-proof');
this.data = await response.json();
}
render() {
this.container.innerHTML = `
<div class="social-proof-widget">
${this.options.showCount ? this.renderUserCount() : ''}
${this.options.showTestimonials ? this.renderTestimonials() : ''}
${this.options.showRecentActivity ? this.renderRecentActivity() : ''}
</div>
`;
}
renderUserCount() {
const { totalUsers, recentSignups } = this.data;
return `
<div class="user-count">
<span class="count">${this.formatNumber(totalUsers)}</span>
<span class="label">marketers trust us</span>
<span class="recent">+${recentSignups} joined this week</span>
</div>
`;
}
renderTestimonials() {
const { testimonials } = this.data;
return `
<div class="testimonials-carousel">
${testimonials.map(t => `
<div class="testimonial">
<img src="${t.avatar}" alt="${t.name}" />
<blockquote>"${t.quote}"</blockquote>
<cite>— ${t.name}, ${t.title} at ${t.company}</cite>
<div class="rating">${'★'.repeat(t.rating)}${'☆'.repeat(5 - t.rating)}</div>
</div>
`).join('')}
</div>
`;
}
renderRecentActivity() {
return `<div class="recent-activity" id="activity-feed"></div>`;
}
startRealtimeNotifications() {
const activities = [
{ action: 'signed up', location: 'San Francisco, CA' },
{ action: 'started a trial', location: 'London, UK' },
{ action: 'upgraded to Pro', location: 'Sydney, AU' },
{ action: 'joined', location: 'Toronto, CA' }
];
const names = ['Sarah', 'Michael', 'David', 'Emma', 'James', 'Olivia'];
setInterval(() => {
const name = names[Math.floor(Math.random() * names.length)];
const activity = activities[Math.floor(Math.random() * activities.length)];
const minutes = Math.floor(Math.random() * 10) + 1;
this.showNotification(
`${name} from ${activity.location} ${activity.action} ${minutes} min ago`
);
}, this.options.refreshInterval);
}
showNotification(message) {
const feed = document.getElementById('activity-feed');
const notification = document.createElement('div');
notification.className = 'activity-notification';
notification.innerHTML = `
<span class="pulse"></span>
<span class="message">${message}</span>
`;
feed.prepend(notification);
setTimeout(() => {
notification.classList.add('visible');
}, 100);
setTimeout(() => {
notification.classList.remove('visible');
setTimeout(() => notification.remove(), 300);
}, 5000);
}
formatNumber(num) {
if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';
if (num >= 1000) return (num / 1000).toFixed(0) + 'K';
return num.toString();
}
}
// Initialize
new SocialProofWidget(document.getElementById('social-proof'), {
showCount: true,
showRecentActivity: true,
refreshInterval: 20000
});Mobile CRO
Mobile-Specific Optimization
code
interface MobileCROChecklist {
design: {
tapTargets: 'Minimum 44x44px touch targets';
thumbZone: 'Primary actions in thumb-friendly zones';
simplifiedNav: 'Hamburger or bottom navigation';
whitespace: 'Adequate spacing between elements';
};
forms: {
inputTypes: 'Use appropriate input types (tel, email, number)';
autofill: 'Support browser autofill';
keyboards: 'Trigger appropriate keyboards';
fieldCount: 'Minimize required fields';
};
checkout: {
guestCheckout: 'Allow purchase without account';
paymentOptions: 'Apple Pay, Google Pay support';
progressIndicator: 'Clear step progression';
savedDetails: 'Remember payment/shipping info';
};
performance: {
imageOptimization: 'WebP with srcset for responsive images';
lazyLoading: 'Below-fold content lazy loaded';
criticalCSS: 'Inline critical CSS';
serviceWorker: 'Offline support for key pages';
};
}Mobile Form Optimization
code
<!-- Optimized mobile form -->
<form id="mobile-signup" class="mobile-optimized-form">
<!-- Email with correct input type -->
<div class="form-group">
<label for="email">Email</label>
<input
type="email"
id="email"
name="email"
autocomplete="email"
inputmode="email"
enterkeyhint="next"
required
/>
</div>
<!-- Phone with numeric keyboard -->
<div class="form-group">
<label for="phone">Phone</label>
<input
type="tel"
id="phone"
name="phone"
autocomplete="tel"
inputmode="tel"
enterkeyhint="next"
pattern="[0-9]*"
/>
</div>
<!-- Large, thumb-friendly submit button -->
<button
type="submit"
class="submit-btn"
style="min-height: 48px; width: 100%;"
>
Start Free Trial
</button>
</form>
<style>
.mobile-optimized-form {
padding: 16px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.form-group input {
width: 100%;
padding: 14px 16px;
font-size: 16px; /* Prevents iOS zoom on focus */
border: 1px solid #ddd;
border-radius: 8px;
-webkit-appearance: none;
}
.form-group input:focus {
outline: none;
border-color: #0066cc;
box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);
}
.submit-btn {
background: #0066cc;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.submit-btn:active {
transform: scale(0.98);
}
</style>CRO Tech Stack
Recommended Tools by Category
code
┌─────────────────────────────────────────────────────────────────┐
│ CRO TOOL ECOSYSTEM │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ANALYTICS TESTING HEATMAPS │
│ ───────── ─────── ──────── │
│ • GA4 • Optimizely • Hotjar │
│ • Mixpanel • VWO • FullStory │
│ • Amplitude • AB Tasty • Microsoft │
│ • Heap • Google Optimize* Clarity │
│ • Kameleoon • Lucky Orange │
│ │
│ SURVEYS PERSONALIZATION SESSION REPLAY │
│ ─────── ─────────────── ────────────── │
│ • Hotjar • Dynamic Yield • FullStory │
│ • Qualaroo • Monetate • LogRocket │
│ • SurveyMonkey • Evergage • Heap │
│ • Typeform • Mutiny • Smartlook │
│ │
│ * Google Optimize sunset - use alternatives │
└─────────────────────────────────────────────────────────────────┘CRO Reporting Dashboard
Server-Side Analytics Query
code
import { createClient } from '@supabase/supabase-js';
interface CRODashboardMetrics {
conversionRate: number;
conversionRateChange: number;
totalConversions: number;
revenue: number;
avgOrderValue: number;
bounceRate: number;
funnelMetrics: FunnelStage[];
topPerformingPages: PageMetric[];
deviceBreakdown: DeviceMetric[];
}
async function getCRODashboard(
dateRange: { start: string; end: string },
comparisonRange: { start: string; end: string }
): Promise<CRODashboardMetrics> {
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_KEY!
);
// Current period metrics
const { data: currentMetrics } = await supabase.rpc('get_cro_metrics', {
start_date: dateRange.start,
end_date: dateRange.end
});
// Comparison period
const { data: previousMetrics } = await supabase.rpc('get_cro_metrics', {
start_date: comparisonRange.start,
end_date: comparisonRange.end
});
// Funnel analysis
const { data: funnelData } = await supabase
.from('funnel_events')
.select('step_name, count')
.gte('timestamp', dateRange.start)
.lte('timestamp', dateRange.end)
.order('step_order');
// Top pages by conversion
const { data: topPages } = await supabase
.from('page_conversions')
.select('page_path, visitors, conversions')
.gte('date', dateRange.start)
.lte('date', dateRange.end)
.order('conversions', { ascending: false })
.limit(10);
return {
conversionRate: currentMetrics.conversion_rate,
conversionRateChange: calculateChange(
currentMetrics.conversion_rate,
previousMetrics.conversion_rate
),
totalConversions: currentMetrics.total_conversions,
revenue: currentMetrics.total_revenue,
avgOrderValue: currentMetrics.avg_order_value,
bounceRate: currentMetrics.bounce_rate,
funnelMetrics: processFunnelData(funnelData),
topPerformingPages: topPages,
deviceBreakdown: currentMetrics.device_breakdown
};
}
function calculateChange(current: number, previous: number): number {
if (previous === 0) return 0;
return ((current - previous) / previous) * 100;
}SQL for CRO Metrics
code
-- Create CRO metrics function
CREATE OR REPLACE FUNCTION get_cro_metrics(
start_date TIMESTAMP,
end_date TIMESTAMP
)
RETURNS JSON AS $$
DECLARE
result JSON;
BEGIN
WITH metrics AS (
SELECT
COUNT(DISTINCT session_id) as total_sessions,
COUNT(DISTINCT CASE WHEN converted = true THEN session_id END) as conversions,
SUM(CASE WHEN converted = true THEN revenue ELSE 0 END) as total_revenue,
AVG(CASE WHEN converted = true THEN revenue END) as avg_order_value,
COUNT(DISTINCT CASE WHEN page_views = 1 THEN session_id END)::float /
NULLIF(COUNT(DISTINCT session_id), 0) * 100 as bounce_rate
FROM sessions
WHERE created_at BETWEEN start_date AND end_date
),
device_breakdown AS (
SELECT
device_type,
COUNT(DISTINCT session_id) as sessions,
COUNT(DISTINCT CASE WHEN converted = true THEN session_id END) as conversions
FROM sessions
WHERE created_at BETWEEN start_date AND end_date
GROUP BY device_type
)
SELECT json_build_object(
'total_sessions', m.total_sessions,
'total_conversions', m.conversions,
'conversion_rate', ROUND((m.conversions::float / NULLIF(m.total_sessions, 0) * 100)::numeric, 2),
'total_revenue', ROUND(m.total_revenue::numeric, 2),
'avg_order_value', ROUND(m.avg_order_value::numeric, 2),
'bounce_rate', ROUND(m.bounce_rate::numeric, 2),
'device_breakdown', (
SELECT json_agg(json_build_object(
'device', device_type,
'sessions', sessions,
'conversions', conversions,
'rate', ROUND((conversions::float / NULLIF(sessions, 0) * 100)::numeric, 2)
))
FROM device_breakdown
)
) INTO result
FROM metrics m;
RETURN result;
END;
$$ LANGUAGE plpgsql;Best Practices Checklist
code
## CRO Implementation Checklist
### Research & Analysis
□ Set up GA4 with enhanced ecommerce
□ Install heatmap/session recording tool
□ Configure funnel visualization
□ Implement user surveys (exit intent, post-purchase)
□ Audit current conversion paths
### Technical Foundation
□ Core Web Vitals all passing
□ Mobile responsiveness verified
□ Cross-browser testing complete
□ Form validation user-friendly
□ Error handling graceful
### Testing Infrastructure
□ A/B testing platform configured
□ Statistical significance calculator ready
□ Test documentation template created
□ QA process for variants defined
□ Rollback procedure documented
### Optimization Areas
□ Landing page copy and design
□ Form field reduction
□ CTA button optimization
□ Trust signals placement
□ Checkout flow streamlining
□ Mobile experience enhancement
### Measurement
□ Conversion tracking accurate
□ Revenue attribution working
□ Test velocity targets set
□ Win/loss documentation process
□ Monthly CRO review scheduledNext: Deep dive into A/B Testing methodology and statistical analysis.