- Add 100 blog posts covering AI, development, and tech topics - Add .env.example for environment configuration - Add accessibility and lighthouse audit scripts - Remove obsolete SEO reports and temporary files - Remove dev-dist build artifacts and backup files - Remove unused portrait images (moved/consolidated elsewhere) - Update contact form and component improvements Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
691 lines
21 KiB
Markdown
691 lines
21 KiB
Markdown
# Conversion Rate Optimization (CRO)
|
||
|
||
**Meta-Description:** Conversion Rate Optimization für SaaS. Landing Pages, User Funnels, Micro-Conversions und datengetriebene Optimierung.
|
||
|
||
**Keywords:** CRO, Conversion Rate, Landing Page, User Funnel, Micro-Conversions, UX Optimization, Growth Hacking
|
||
|
||
---
|
||
|
||
## Einführung
|
||
|
||
**Conversion Rate Optimization (CRO)** maximiert den Wert bestehenden Traffics. Statt mehr Besucher zu kaufen, werden bestehende Nutzer besser konvertiert. Dieser Guide zeigt systematische Ansätze für **Landing Pages**, **Funnels** und **Micro-Conversions**.
|
||
|
||
---
|
||
|
||
## CRO Overview
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ CONVERSION OPTIMIZATION FUNNEL │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ TOFU (Top of Funnel): │
|
||
│ ┌─────────────────────────────────────────────────────┐ │
|
||
│ │ Awareness → Interest │ │
|
||
│ │ ├── Landing Page Views │ │
|
||
│ │ ├── Blog Post Reads │ │
|
||
│ │ ├── Social Media Clicks │ │
|
||
│ │ └── Target: Reduce Bounce Rate │ │
|
||
│ └─────────────────────────────────────────────────────┘ │
|
||
│ ↓ │
|
||
│ MOFU (Middle of Funnel): │
|
||
│ ┌─────────────────────────────────────────────────────┐ │
|
||
│ │ Consideration → Intent │ │
|
||
│ │ ├── Email Signups │ │
|
||
│ │ ├── Free Trial Starts │ │
|
||
│ │ ├── Demo Requests │ │
|
||
│ │ └── Target: Increase Lead Quality │ │
|
||
│ └─────────────────────────────────────────────────────┘ │
|
||
│ ↓ │
|
||
│ BOFU (Bottom of Funnel): │
|
||
│ ┌─────────────────────────────────────────────────────┐ │
|
||
│ │ Purchase → Retention │ │
|
||
│ │ ├── Trial to Paid Conversion │ │
|
||
│ │ ├── Checkout Completion │ │
|
||
│ │ ├── Upsells / Cross-sells │ │
|
||
│ │ └── Target: Maximize Revenue │ │
|
||
│ └─────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ Key Metrics: │
|
||
│ ├── Conversion Rate = Conversions / Visitors × 100 │
|
||
│ ├── Micro-Conversion Rate (Email, Trial, etc.) │
|
||
│ ├── Revenue per Visitor (RPV) │
|
||
│ └── Customer Acquisition Cost (CAC) │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## Landing Page Optimization
|
||
|
||
```tsx
|
||
// components/landing/OptimizedHero.tsx
|
||
'use client';
|
||
|
||
import { useState } from 'react';
|
||
import { track } from '@/lib/analytics';
|
||
|
||
interface HeroProps {
|
||
headline: string;
|
||
subheadline: string;
|
||
ctaText: string;
|
||
ctaUrl: string;
|
||
trustBadges: string[];
|
||
videoUrl?: string;
|
||
}
|
||
|
||
export function OptimizedHero({
|
||
headline,
|
||
subheadline,
|
||
ctaText,
|
||
ctaUrl,
|
||
trustBadges,
|
||
videoUrl
|
||
}: HeroProps) {
|
||
const [videoPlaying, setVideoPlaying] = useState(false);
|
||
|
||
const handleCTAClick = () => {
|
||
track('hero_cta_clicked', {
|
||
headline,
|
||
ctaText
|
||
});
|
||
};
|
||
|
||
const handleVideoPlay = () => {
|
||
setVideoPlaying(true);
|
||
track('hero_video_played');
|
||
};
|
||
|
||
return (
|
||
<section className="relative py-20 lg:py-32">
|
||
<div className="container mx-auto px-4">
|
||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||
{/* Copy Side */}
|
||
<div className="space-y-6">
|
||
{/* Social Proof Mini */}
|
||
<div className="flex items-center gap-2 text-sm text-gray-600">
|
||
<span className="flex -space-x-2">
|
||
{[1, 2, 3, 4, 5].map(i => (
|
||
<img
|
||
key={i}
|
||
src={`/avatars/${i}.jpg`}
|
||
alt=""
|
||
className="w-8 h-8 rounded-full border-2 border-white"
|
||
/>
|
||
))}
|
||
</span>
|
||
<span>Join 10,000+ happy customers</span>
|
||
</div>
|
||
|
||
{/* Headline - Max 6 words */}
|
||
<h1 className="text-4xl lg:text-6xl font-bold leading-tight">
|
||
{headline}
|
||
</h1>
|
||
|
||
{/* Subheadline - Benefits focused */}
|
||
<p className="text-xl text-gray-600 max-w-lg">
|
||
{subheadline}
|
||
</p>
|
||
|
||
{/* CTA Group */}
|
||
<div className="flex flex-col sm:flex-row gap-4">
|
||
<a
|
||
href={ctaUrl}
|
||
onClick={handleCTAClick}
|
||
className="btn-primary text-lg px-8 py-4 text-center"
|
||
>
|
||
{ctaText}
|
||
</a>
|
||
<span className="text-sm text-gray-500 self-center">
|
||
No credit card required
|
||
</span>
|
||
</div>
|
||
|
||
{/* Trust Badges */}
|
||
<div className="flex flex-wrap gap-4 pt-4">
|
||
{trustBadges.map((badge, i) => (
|
||
<div
|
||
key={i}
|
||
className="flex items-center gap-2 text-sm text-gray-600"
|
||
>
|
||
<CheckIcon className="w-5 h-5 text-green-500" />
|
||
{badge}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Visual Side */}
|
||
<div className="relative">
|
||
{videoUrl ? (
|
||
<VideoPlayer
|
||
url={videoUrl}
|
||
onPlay={handleVideoPlay}
|
||
playing={videoPlaying}
|
||
/>
|
||
) : (
|
||
<img
|
||
src="/hero-image.webp"
|
||
alt="Product screenshot"
|
||
className="rounded-lg shadow-2xl"
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|
||
```
|
||
|
||
```tsx
|
||
// components/landing/SocialProof.tsx
|
||
interface Testimonial {
|
||
quote: string;
|
||
author: string;
|
||
role: string;
|
||
company: string;
|
||
avatar: string;
|
||
logo: string;
|
||
metric?: string;
|
||
}
|
||
|
||
export function SocialProofSection({
|
||
testimonials
|
||
}: {
|
||
testimonials: Testimonial[]
|
||
}) {
|
||
return (
|
||
<section className="py-20 bg-gray-50">
|
||
<div className="container mx-auto px-4">
|
||
{/* Logos Bar */}
|
||
<div className="text-center mb-12">
|
||
<p className="text-sm text-gray-500 mb-6">
|
||
Trusted by leading companies
|
||
</p>
|
||
<div className="flex flex-wrap justify-center items-center gap-8 opacity-60">
|
||
{testimonials.map((t, i) => (
|
||
<img
|
||
key={i}
|
||
src={t.logo}
|
||
alt={t.company}
|
||
className="h-8 grayscale hover:grayscale-0 transition"
|
||
/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Testimonials Grid */}
|
||
<div className="grid md:grid-cols-3 gap-8">
|
||
{testimonials.map((testimonial, i) => (
|
||
<TestimonialCard key={i} {...testimonial} />
|
||
))}
|
||
</div>
|
||
|
||
{/* Aggregate Stats */}
|
||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 mt-16 text-center">
|
||
<Stat value="10,000+" label="Active Users" />
|
||
<Stat value="99.9%" label="Uptime" />
|
||
<Stat value="4.9/5" label="Customer Rating" />
|
||
<Stat value="24/7" label="Support" />
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
function TestimonialCard({
|
||
quote,
|
||
author,
|
||
role,
|
||
company,
|
||
avatar,
|
||
metric
|
||
}: Testimonial) {
|
||
return (
|
||
<div className="bg-white p-6 rounded-xl shadow-sm">
|
||
{metric && (
|
||
<div className="text-3xl font-bold text-blue-600 mb-4">
|
||
{metric}
|
||
</div>
|
||
)}
|
||
<blockquote className="text-gray-700 mb-4">
|
||
"{quote}"
|
||
</blockquote>
|
||
<div className="flex items-center gap-3">
|
||
<img
|
||
src={avatar}
|
||
alt={author}
|
||
className="w-12 h-12 rounded-full"
|
||
/>
|
||
<div>
|
||
<div className="font-semibold">{author}</div>
|
||
<div className="text-sm text-gray-500">{role}, {company}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Funnel Tracking
|
||
|
||
```typescript
|
||
// lib/cro/funnel.ts
|
||
export interface FunnelStep {
|
||
id: string;
|
||
name: string;
|
||
pagePattern: string | RegExp;
|
||
requiredEvents?: string[];
|
||
}
|
||
|
||
export interface FunnelConfig {
|
||
id: string;
|
||
name: string;
|
||
steps: FunnelStep[];
|
||
}
|
||
|
||
export const signupFunnel: FunnelConfig = {
|
||
id: 'signup',
|
||
name: 'Signup Funnel',
|
||
steps: [
|
||
{ id: 'landing', name: 'Landing Page', pagePattern: '/' },
|
||
{ id: 'pricing', name: 'Pricing Page', pagePattern: '/pricing' },
|
||
{ id: 'signup-start', name: 'Signup Started', pagePattern: '/signup', requiredEvents: ['signup_form_viewed'] },
|
||
{ id: 'signup-complete', name: 'Signup Complete', pagePattern: '/welcome', requiredEvents: ['signup_completed'] },
|
||
{ id: 'onboarding', name: 'Onboarding', pagePattern: '/onboarding' },
|
||
{ id: 'activated', name: 'Activated', pagePattern: '/dashboard', requiredEvents: ['first_action_completed'] }
|
||
]
|
||
};
|
||
|
||
// Funnel Analytics
|
||
export async function getFunnelAnalytics(
|
||
funnelId: string,
|
||
startDate: Date,
|
||
endDate: Date
|
||
): Promise<FunnelAnalytics> {
|
||
const funnel = funnels.get(funnelId);
|
||
if (!funnel) throw new Error(`Funnel ${funnelId} not found`);
|
||
|
||
const stepData = await Promise.all(
|
||
funnel.steps.map(async (step, index) => {
|
||
const count = await getStepCount(step, startDate, endDate);
|
||
const previousCount = index > 0
|
||
? await getStepCount(funnel.steps[index - 1], startDate, endDate)
|
||
: count;
|
||
|
||
return {
|
||
step: step.name,
|
||
count,
|
||
conversionRate: previousCount > 0 ? (count / previousCount) * 100 : 100,
|
||
dropoffRate: previousCount > 0 ? ((previousCount - count) / previousCount) * 100 : 0
|
||
};
|
||
})
|
||
);
|
||
|
||
const overallConversion = stepData[0].count > 0
|
||
? (stepData[stepData.length - 1].count / stepData[0].count) * 100
|
||
: 0;
|
||
|
||
return {
|
||
funnel: funnel.name,
|
||
period: { start: startDate, end: endDate },
|
||
steps: stepData,
|
||
overallConversion
|
||
};
|
||
}
|
||
```
|
||
|
||
```tsx
|
||
// components/analytics/FunnelVisualization.tsx
|
||
'use client';
|
||
|
||
interface FunnelStep {
|
||
step: string;
|
||
count: number;
|
||
conversionRate: number;
|
||
dropoffRate: number;
|
||
}
|
||
|
||
export function FunnelVisualization({ steps }: { steps: FunnelStep[] }) {
|
||
const maxCount = Math.max(...steps.map(s => s.count));
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
{steps.map((step, index) => {
|
||
const width = (step.count / maxCount) * 100;
|
||
const isLastStep = index === steps.length - 1;
|
||
|
||
return (
|
||
<div key={step.step} className="relative">
|
||
{/* Step Bar */}
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-32 text-sm font-medium">{step.step}</div>
|
||
<div className="flex-1 h-12 bg-gray-100 rounded-lg overflow-hidden">
|
||
<div
|
||
className="h-full bg-blue-500 flex items-center justify-end px-4 transition-all"
|
||
style={{ width: `${width}%` }}
|
||
>
|
||
<span className="text-white font-semibold">
|
||
{step.count.toLocaleString()}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div className="w-20 text-right">
|
||
<span className={`text-sm font-medium ${
|
||
step.conversionRate >= 50 ? 'text-green-600' : 'text-red-600'
|
||
}`}>
|
||
{step.conversionRate.toFixed(1)}%
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Dropoff Indicator */}
|
||
{!isLastStep && step.dropoffRate > 0 && (
|
||
<div className="ml-32 pl-4 py-2 text-sm text-red-500">
|
||
↳ {step.dropoffRate.toFixed(1)}% dropoff ({Math.round(step.count * step.dropoffRate / 100).toLocaleString()} users)
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Micro-Conversions
|
||
|
||
```typescript
|
||
// lib/cro/micro-conversions.ts
|
||
export const microConversions = {
|
||
engagement: [
|
||
{ id: 'scroll_depth_50', name: 'Scrolled 50%', weight: 1 },
|
||
{ id: 'scroll_depth_90', name: 'Scrolled 90%', weight: 2 },
|
||
{ id: 'time_on_page_60s', name: '60s on page', weight: 2 },
|
||
{ id: 'video_played', name: 'Video Played', weight: 3 },
|
||
{ id: 'video_completed', name: 'Video Completed', weight: 5 }
|
||
],
|
||
intent: [
|
||
{ id: 'pricing_viewed', name: 'Viewed Pricing', weight: 5 },
|
||
{ id: 'feature_comparison', name: 'Compared Features', weight: 4 },
|
||
{ id: 'faq_expanded', name: 'Expanded FAQ', weight: 2 },
|
||
{ id: 'live_chat_started', name: 'Started Live Chat', weight: 8 }
|
||
],
|
||
lead: [
|
||
{ id: 'email_captured', name: 'Email Signup', weight: 10 },
|
||
{ id: 'demo_requested', name: 'Demo Request', weight: 15 },
|
||
{ id: 'trial_started', name: 'Trial Started', weight: 20 }
|
||
]
|
||
};
|
||
|
||
// Calculate Lead Score
|
||
export function calculateLeadScore(events: string[]): number {
|
||
let score = 0;
|
||
|
||
Object.values(microConversions).flat().forEach(conversion => {
|
||
if (events.includes(conversion.id)) {
|
||
score += conversion.weight;
|
||
}
|
||
});
|
||
|
||
return score;
|
||
}
|
||
|
||
// hooks/useMicroConversions.ts
|
||
'use client';
|
||
|
||
import { useEffect, useCallback } from 'react';
|
||
import { track } from '@/lib/analytics';
|
||
|
||
export function useMicroConversions() {
|
||
// Scroll Depth Tracking
|
||
useEffect(() => {
|
||
let maxScroll = 0;
|
||
const milestones = [25, 50, 75, 90];
|
||
const triggered = new Set<number>();
|
||
|
||
const handleScroll = () => {
|
||
const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
|
||
const scrollPercent = Math.round((window.scrollY / scrollHeight) * 100);
|
||
|
||
if (scrollPercent > maxScroll) {
|
||
maxScroll = scrollPercent;
|
||
|
||
milestones.forEach(milestone => {
|
||
if (scrollPercent >= milestone && !triggered.has(milestone)) {
|
||
triggered.add(milestone);
|
||
track(`scroll_depth_${milestone}`);
|
||
}
|
||
});
|
||
}
|
||
};
|
||
|
||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||
return () => window.removeEventListener('scroll', handleScroll);
|
||
}, []);
|
||
|
||
// Time on Page Tracking
|
||
useEffect(() => {
|
||
const milestones = [30, 60, 120, 300];
|
||
let elapsed = 0;
|
||
|
||
const interval = setInterval(() => {
|
||
elapsed += 1;
|
||
|
||
if (milestones.includes(elapsed)) {
|
||
track(`time_on_page_${elapsed}s`);
|
||
}
|
||
}, 1000);
|
||
|
||
return () => clearInterval(interval);
|
||
}, []);
|
||
|
||
// Track specific actions
|
||
const trackMicro = useCallback((eventId: string, metadata?: Record<string, unknown>) => {
|
||
track(eventId, metadata);
|
||
}, []);
|
||
|
||
return { trackMicro };
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Form Optimization
|
||
|
||
```tsx
|
||
// components/forms/OptimizedSignupForm.tsx
|
||
'use client';
|
||
|
||
import { useState, useEffect } from 'react';
|
||
import { useForm } from 'react-hook-form';
|
||
import { zodResolver } from '@hookform/resolvers/zod';
|
||
import { z } from 'zod';
|
||
import { track } from '@/lib/analytics';
|
||
|
||
const signupSchema = z.object({
|
||
email: z.string().email('Please enter a valid email'),
|
||
password: z.string().min(8, 'Password must be at least 8 characters'),
|
||
name: z.string().min(2, 'Name is required')
|
||
});
|
||
|
||
type SignupData = z.infer<typeof signupSchema>;
|
||
|
||
export function OptimizedSignupForm() {
|
||
const [step, setStep] = useState(1);
|
||
const [startTime] = useState(Date.now());
|
||
|
||
const {
|
||
register,
|
||
handleSubmit,
|
||
formState: { errors, isSubmitting },
|
||
watch
|
||
} = useForm<SignupData>({
|
||
resolver: zodResolver(signupSchema),
|
||
mode: 'onBlur'
|
||
});
|
||
|
||
// Track form interactions
|
||
useEffect(() => {
|
||
track('signup_form_viewed');
|
||
}, []);
|
||
|
||
const email = watch('email');
|
||
|
||
useEffect(() => {
|
||
if (email && email.includes('@')) {
|
||
track('signup_email_entered');
|
||
}
|
||
}, [email]);
|
||
|
||
const onSubmit = async (data: SignupData) => {
|
||
const timeToComplete = Date.now() - startTime;
|
||
|
||
track('signup_form_submitted', {
|
||
timeToComplete,
|
||
step
|
||
});
|
||
|
||
try {
|
||
await createAccount(data);
|
||
track('signup_completed', { timeToComplete });
|
||
} catch (error) {
|
||
track('signup_error', { error: (error as Error).message });
|
||
}
|
||
};
|
||
|
||
// Field focus tracking
|
||
const trackFieldFocus = (field: string) => {
|
||
track('form_field_focused', { field });
|
||
};
|
||
|
||
return (
|
||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
||
{/* Progress Indicator */}
|
||
<div className="flex gap-2 mb-6">
|
||
{[1, 2, 3].map(s => (
|
||
<div
|
||
key={s}
|
||
className={`h-1 flex-1 rounded ${
|
||
s <= step ? 'bg-blue-600' : 'bg-gray-200'
|
||
}`}
|
||
/>
|
||
))}
|
||
</div>
|
||
|
||
{/* Step 1: Email */}
|
||
{step >= 1 && (
|
||
<div>
|
||
<label className="block text-sm font-medium mb-2">
|
||
Work Email
|
||
</label>
|
||
<input
|
||
type="email"
|
||
{...register('email')}
|
||
onFocus={() => trackFieldFocus('email')}
|
||
className="w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500"
|
||
placeholder="you@company.com"
|
||
autoComplete="email"
|
||
/>
|
||
{errors.email && (
|
||
<p className="text-red-500 text-sm mt-1">{errors.email.message}</p>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
{/* Step 2: Password */}
|
||
{step >= 2 && (
|
||
<div>
|
||
<label className="block text-sm font-medium mb-2">
|
||
Create Password
|
||
</label>
|
||
<input
|
||
type="password"
|
||
{...register('password')}
|
||
onFocus={() => trackFieldFocus('password')}
|
||
className="w-full px-4 py-3 border rounded-lg"
|
||
placeholder="8+ characters"
|
||
/>
|
||
{errors.password && (
|
||
<p className="text-red-500 text-sm mt-1">{errors.password.message}</p>
|
||
)}
|
||
{/* Password Strength */}
|
||
<PasswordStrengthMeter password={watch('password') || ''} />
|
||
</div>
|
||
)}
|
||
|
||
{/* CTA Button */}
|
||
<button
|
||
type={step < 2 ? 'button' : 'submit'}
|
||
onClick={() => step < 2 && setStep(step + 1)}
|
||
disabled={isSubmitting}
|
||
className="w-full py-4 bg-blue-600 text-white rounded-lg font-semibold hover:bg-blue-700 disabled:opacity-50"
|
||
>
|
||
{isSubmitting ? 'Creating account...' : step < 2 ? 'Continue' : 'Create Free Account'}
|
||
</button>
|
||
|
||
{/* Trust Signals */}
|
||
<div className="flex items-center justify-center gap-4 text-sm text-gray-500">
|
||
<span className="flex items-center gap-1">
|
||
<LockIcon className="w-4 h-4" />
|
||
Secure
|
||
</span>
|
||
<span>No credit card required</span>
|
||
<span>Cancel anytime</span>
|
||
</div>
|
||
</form>
|
||
);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## CRO Checklist
|
||
|
||
| Element | Best Practice |
|
||
|---------|--------------|
|
||
| **Headline** | Clear value proposition, max 6 words |
|
||
| **CTA** | Action-oriented, contrasting color |
|
||
| **Social Proof** | Numbers, logos, testimonials |
|
||
| **Trust Signals** | Security badges, guarantees |
|
||
| **Forms** | Progressive, minimal fields |
|
||
| **Mobile** | Touch-friendly, fast loading |
|
||
| **Urgency** | Scarcity, limited time (honest!) |
|
||
| **Exit Intent** | Offer value, not desperation |
|
||
|
||
---
|
||
|
||
## Fazit
|
||
|
||
Conversion Rate Optimization erfordert:
|
||
|
||
1. **Datenanalyse**: Funnels und Micro-Conversions tracken
|
||
2. **User Research**: Verstehen warum Nutzer konvertieren
|
||
3. **Testing**: Systematisches A/B Testing
|
||
4. **Iteration**: Kontinuierliche Verbesserung
|
||
|
||
Kleine Verbesserungen summieren sich zu großen Ergebnissen.
|
||
|
||
---
|
||
|
||
## Bildprompts
|
||
|
||
1. "Conversion funnel visualization, colorful stages with dropoff points"
|
||
2. "Landing page wireframe with CRO annotations, best practices highlighted"
|
||
3. "A/B test results dashboard, winner variant celebration"
|
||
|
||
---
|
||
|
||
## Quellen
|
||
|
||
- [CXL Conversion Optimization Guide](https://cxl.com/blog/conversion-optimization-guide/)
|
||
- [Unbounce Landing Page Best Practices](https://unbounce.com/landing-page-articles/)
|
||
- [Hotjar CRO Resources](https://www.hotjar.com/conversion-rate-optimization/)
|
||
- [Neil Patel CRO Guide](https://neilpatel.com/blog/conversion-rate-optimization/)
|