Product-Led Growth (PLG) Implementation Roadmap
Version: 1.0 Date: December 28, 2024 Timeline: 6 months to PLG fundamentals Status: Ready for Review
🚨 CRITICAL PRE-LAUNCH UPDATE (Dec 28, 2024):
Juniro is launching in 45 days (mid-February 2026) with no existing user base.
This roadmap represents the 6-month post-launch plan and assumes you have launched successfully with initial traction.
FIRST, complete the pre-launch plan: See PLG_PRE_LAUNCH_STRATEGY.md for the 45-day countdown to launch (provider recruitment, beta testing, waitlist building).
THEN, execute this roadmap starting from your actual launch date (Feb 2026).
Key adjustments for pre-launch context:
- References to "existing users" should read as "beta users" for first 6 weeks
- Social proof metrics (e.g., "12,000+ families") don't exist yet - use authentic beta numbers instead
- Viral growth (Month 2) will start slower without existing user base - expect 3-4 months to reach k-factor > 1.0
- SEO content (Month 3) should begin BEFORE launch to have rankings by Month 3 post-launch
📋 Table of Contents
- Quick Wins (Week 1-2)
- Phase 1: Foundation (Month 1)
- Phase 2: Viral Growth (Month 2)
- Phase 3: Content Engine (Month 3)
- Phase 4: Optimization (Month 4-5)
- Phase 5: Scale (Month 6)
- Success Metrics
- Resource Requirements
🚀 Quick Wins (Week 1-2)
Goal: Ship high-impact, low-effort improvements immediately
Target Metrics:
- 30-50% improvement in homepage engagement
- 20-30% improvement in signup rate
- Baseline metrics established
QW-1: Rewrite Homepage Value Proposition
Priority: P0 - Critical Effort: 2 hours Impact: High
Current:
"Find the perfect activities for your children"
Recommended Changes:
Option A (Time-Savings Focused):
"Book children's activities in 2 clicks, not 2 hours"
Subheadline: "550+ verified providers. All schedules,
prices, and reviews in one search. Free for parents."
Option B (Problem-Solution Focused):
"Stop juggling 10 websites. All kids' activities in one search."
Subheadline: "Piano, soccer, art, STEM - everything your
child needs, all in one place. Verified & background-checked."
Option C (Social Proof Focused):
"Join 12,000+ parents who found the perfect activities"
Subheadline: "Search 550+ providers, compare schedules & prices,
book instantly. Built from what parents told us."
Tasks:
- A/B test all 3 options (split traffic 25/25/25/25 with control)
- Run for 7 days minimum (statistical significance)
- Measure: Click-through rate on primary CTA
- Ship winning variant
Acceptance Criteria:
- New headline tested in production
- Analytics tracking implemented
- Winner selected based on data
- Improvement > 20% over control
Owner: Product Manager + Copywriter Timeline: Week 1
QW-2: Add Social Proof Numbers
Priority: P0 - Critical Effort: 4 hours Impact: High
Implementation:
Location 1: Hero Section
<div className="trust-stats">
<div className="stat">
<span className="number">12,000+</span>
<span className="label">Happy Families</span>
</div>
<div className="stat">
<span className="number">550+</span>
<span className="label">Verified Providers</span>
</div>
<div className="stat">
<span className="number">45,000+</span>
<span className="label">Bookings Completed</span>
</div>
</div>
Location 2: Trust Badge Section
<div className="trust-badges">
<img src="/badges/bbb-accredited.png" alt="BBB Accredited" />
<img src="/badges/background-check-verified.png" alt="Background Checked" />
<img src="/badges/secure-payment.png" alt="Secure Payments" />
<img src="/badges/insurance-verified.png" alt="Insured Providers" />
</div>
Tasks:
- Pull real numbers from database (or calculate estimates)
- Design trust stat component
- Implement in HomePage.tsx
- Add trust badge section
- Create/source badge images
Acceptance Criteria:
- Numbers are accurate (within 10%)
- Auto-updating (not hardcoded)
- Trust badges visible and professional
- Mobile responsive
- Accessible (proper alt text)
Owner: Frontend Developer + Designer Timeline: Week 1
QW-3: Add Real Parent Testimonials
Priority: P0 - Critical Effort: 6 hours (content) + 4 hours (implementation) Impact: High
Content Required: Collect 3-5 testimonials with:
- Parent name
- Child age
- Activity booked
- Photo (parent + child, or just parent)
- Quote (specific, not generic)
Good Example:
"We tried 3 piano teachers before finding Ms. Priya on Juniro.
Emma's been taking lessons for 6 months and actually practices
without being asked now. The search filters by schedule saved
me hours of calling around."
— Anjali R., mother of Emma (7), Banjara Hills
[Photo of Anjali and Emma at piano]
Bad Example:
"Great platform! Highly recommend!"
— Parent
Implementation:
<section className="testimonials">
<h2>What Parents Say</h2>
<div className="testimonial-grid">
{testimonials.map(testimonial => (
<TestimonialCard
quote={testimonial.quote}
parentName={testimonial.parentName}
childName={testimonial.childName}
childAge={testimonial.childAge}
location={testimonial.location}
photo={testimonial.photo}
activity={testimonial.activity}
/>
))}
</div>
</section>
Tasks:
- Reach out to existing happy customers
- Get written permission (photo + quote usage)
- Professional photo editing (if needed)
- Create TestimonialCard component
- Implement on homepage (below fold)
- Add schema markup for SEO (reviews)
Acceptance Criteria:
- 3 minimum, 5 ideal testimonials
- High-quality photos
- Specific, detailed quotes (not generic)
- Legal consent obtained
- Mobile responsive display
Owner: Marketing Manager (content) + Frontend Developer (implementation) Timeline: Week 1-2
QW-4: Implement "Browse Without Signup"
Priority: P0 - Critical Effort: 8 hours Impact: Very High
Current Flow:
Search → Results → Click Activity → SIGNUP WALL → Details
↑
60% drop-off
New Flow:
Search → Results → Click Activity → Full Details → Add to Wishlist → Continue browsing
↓
Signup only when ready to BOOK
Implementation:
Step 1: Remove auth gates from detail pages
// Before
const ActivityDetail = () => {
const { user } = useAuth();
if (!user) {
return <SignupPrompt />;
}
return <ActivityDetails />;
};
// After
const ActivityDetail = () => {
return <ActivityDetails />; // No auth check
};
Step 2: Add "Wishlist" feature (no signup required)
// Use localStorage for non-authenticated users
const useWishlist = () => {
const { user } = useAuth();
const addToWishlist = (activityId) => {
if (user) {
// Save to database
api.wishlist.add(activityId);
} else {
// Save to localStorage
const wishlist = JSON.parse(localStorage.getItem('wishlist') || '[]');
wishlist.push(activityId);
localStorage.setItem('wishlist', JSON.stringify(wishlist));
}
};
return { addToWishlist };
};
Step 3: Show signup modal only at booking
const BookingButton = ({ activity }) => {
const { user } = useAuth();
const [showAuthModal, setShowAuthModal] = useState(false);
const handleBook = () => {
if (!user) {
setShowAuthModal(true); // Prompt signup
} else {
proceedToBooking(activity);
}
};
return (
<>
<Button onClick={handleBook}>Book Now</Button>
{showAuthModal && <AuthModal onClose={() => setShowAuthModal(false)} />}
</>
);
};
Step 4: Migrate localStorage wishlist on signup
const onSignupSuccess = async (user) => {
const localWishlist = JSON.parse(localStorage.getItem('wishlist') || '[]');
if (localWishlist.length > 0) {
// Migrate to user account
await api.wishlist.bulkAdd(user.id, localWishlist);
localStorage.removeItem('wishlist');
}
};
Tasks:
- Remove auth gates from:
- Activity detail pages
- Provider profile pages
- Search results
- Implement localStorage wishlist
- Add "Sign up to book" modal (triggered at booking only)
- Implement wishlist migration on signup
- Add analytics tracking (browsing depth before signup)
Acceptance Criteria:
- Users can browse all activities without signup
- Wishlist works without account (localStorage)
- Signup modal appears only at booking
- Wishlist migrates seamlessly on signup
- No data loss during migration
- Analytics tracking implemented
Owner: Senior Frontend Developer Timeline: Week 2
Expected Impact:
- 60-70% reduction in early drop-off
- 3-4x increase in pages per session
- 40-50% improvement in signup rate (higher intent users)
QW-5: Setup Analytics & Tracking
Priority: P0 - Critical Effort: 12 hours Impact: High (enables all future optimizations)
Tools Required:
- Google Analytics 4 (GA4)
- Mixpanel or Amplitude (product analytics)
- Hotjar or FullStory (session recording)
Events to Track:
Acquisition:
// Homepage events
trackEvent('homepage_viewed', { referrer, utm_source, utm_medium });
trackEvent('hero_cta_clicked', { cta_text, position });
trackEvent('search_initiated', { query, location });
// Signup events
trackEvent('signup_started', { source_page });
trackEvent('signup_completed', { method, time_to_complete });
trackEvent('signup_abandoned', { step, time_spent });
Activation:
// First session events
trackEvent('first_search', { query, filters_used });
trackEvent('first_activity_viewed', { activity_id, time_to_first_view });
trackEvent('wishlist_item_added', { activity_id, is_authenticated });
trackEvent('first_booking_completed', { activity_id, time_from_signup });
// Aha moment
trackEvent('aha_moment', {
definition: 'viewed_3_activities_in_5_minutes',
timestamp
});
Engagement:
// Session events
trackEvent('search_performed', { query, filters, results_count });
trackEvent('filter_applied', { filter_type, filter_value });
trackEvent('activity_clicked', { activity_id, position, search_query });
trackEvent('provider_profile_viewed', { provider_id, source });
// Interaction depth
trackEvent('scroll_depth', { page, depth_percentage });
trackEvent('time_on_page', { page, seconds });
Conversion:
// Booking funnel
trackEvent('booking_initiated', { activity_id });
trackEvent('booking_payment_info_entered', { activity_id });
trackEvent('booking_completed', {
activity_id,
booking_value,
time_from_search,
payment_method
});
trackEvent('booking_abandoned', { step, activity_id, reason });
Retention:
// Return visits
trackEvent('return_visit', { days_since_signup, visit_number });
trackEvent('email_opened', { campaign_id, subject });
trackEvent('email_clicked', { campaign_id, link });
// Feature adoption
trackEvent('feature_used', { feature_name, usage_count });
trackEvent('wishlist_viewed', { item_count });
Referral/Viral:
// Sharing events
trackEvent('share_button_clicked', { content_type, platform });
trackEvent('referral_link_generated', { user_id });
trackEvent('referral_link_clicked', { referrer_id });
trackEvent('referral_signup_completed', { referrer_id, reward_earned });
Tasks:
- Setup GA4 account and install tracking code
- Setup Mixpanel/Amplitude account
- Implement event tracking library (wrapper)
- Add events to all key user actions
- Setup conversion funnels in analytics tools
- Create dashboards for key metrics
- Setup automated reports (weekly email)
- Train team on analytics tools
Acceptance Criteria:
- All critical events tracked
- Events firing correctly (test in staging)
- Funnels configured (signup, booking, referral)
- Dashboards accessible to team
- Data privacy compliance (GDPR, CCPA)
- Documentation for tracked events
Owner: Data Analyst + Frontend Developers Timeline: Week 1-2
Dashboards to Create:
1. Acquisition Dashboard:
- Traffic sources
- Signup funnel
- CAC by channel
- Top landing pages
2. Activation Dashboard:
- Time to first value
- Aha moment rate
- First booking funnel
- New user cohort analysis
3. Engagement Dashboard:
- DAU/WAU/MAU
- Session duration
- Pages per session
- Feature adoption
4. Retention Dashboard:
- Cohort retention curves
- Churn rate
- LTV by cohort
- Repeat booking rate
5. Viral Dashboard:
- k-factor (viral coefficient)
- Referral conversion rate
- Share button clicks
- Social traffic
📅 Phase 1: Foundation (Month 1)
Goal: Establish PLG fundamentals and measurement infrastructure
Target Metrics:
- 50% improvement in signup rate
- 30% improvement in time-to-value
- Analytics fully operational
P1-1: Build Personalization Onboarding
Priority: P0 Effort: 40 hours Impact: Very High
Current Experience:
Homepage → Generic search → Filter manually → Browse 100+ results
New Experience:
Homepage → "Tell us about your child" (15 seconds)
→ Instantly personalized results
→ "47 activities perfect for Emma (age 7)"
Implementation:
Step 1: Create Onboarding Modal
// components/OnboardingModal.tsx
export const OnboardingModal = ({ onComplete }) => {
const [step, setStep] = useState(1);
const [data, setData] = useState({
childName: '',
childAge: null,
location: '',
interests: []
});
return (
<Modal open={true} size="md">
{step === 1 && (
<Step1Welcome onNext={() => setStep(2)} />
)}
{step === 2 && (
<Step2ChildInfo
data={data}
onChange={setData}
onNext={() => setStep(3)}
/>
)}
{step === 3 && (
<Step3Interests
data={data}
onChange={setData}
onComplete={() => onComplete(data)}
/>
)}
</Modal>
);
};
// Step 2: Child Info
const Step2ChildInfo = ({ data, onChange, onNext }) => {
return (
<div className="onboarding-step">
<h2>Tell us about your child</h2>
<p>We'll show you the perfect activities</p>
<Input
label="Child's Name"
value={data.childName}
onChange={(e) => onChange({ ...data, childName: e.target.value })}
placeholder="Emma"
/>
<Select
label="Age"
value={data.childAge}
onChange={(age) => onChange({ ...data, childAge: age })}
options={[
{ value: 3, label: '3 years old' },
{ value: 4, label: '4 years old' },
// ... up to 18
]}
/>
<LocationInput
value={data.location}
onChange={(location) => onChange({ ...data, location })}
autoDetect={true}
/>
<Button onClick={onNext} disabled={!isValid(data)}>
Continue →
</Button>
</div>
);
};
// Step 3: Interests (optional but helpful)
const Step3Interests = ({ data, onChange, onComplete }) => {
const interests = [
{ id: 'music', label: 'Music & Dance', icon: '🎵' },
{ id: 'sport', label: 'Sports', icon: '⚽' },
{ id: 'art', label: 'Arts & Crafts', icon: '🎨' },
{ id: 'stem', label: 'STEM', icon: '🔬' },
{ id: 'language', label: 'Languages', icon: '🗣️' },
];
return (
<div className="onboarding-step">
<h2>What interests {data.childName}?</h2>
<p>Select all that apply (optional)</p>
<div className="interest-grid">
{interests.map(interest => (
<InterestCard
key={interest.id}
{...interest}
selected={data.interests.includes(interest.id)}
onToggle={() => toggleInterest(interest.id)}
/>
))}
</div>
<Button onClick={() => onComplete(data)} variant="primary">
Show me activities →
</Button>
<Button onClick={() => onComplete(data)} variant="text">
Skip for now
</Button>
</div>
);
};
Step 2: Personalized Results Page
// pages/search/PersonalizedResults.tsx
const PersonalizedResults = ({ childName, childAge, location, interests }) => {
const activities = usePersonalizedActivities({ childAge, location, interests });
return (
<div className="personalized-results">
<h1>We found {activities.length} activities perfect for {childName}</h1>
<section>
<h2>✨ Recommended for {childName}</h2>
<p>Based on age {childAge} and interests</p>
<ActivityGrid activities={activities.recommended} />
</section>
<section>
<h2>🔥 Trending nearby</h2>
<p>Popular in {location}</p>
<ActivityGrid activities={activities.trending} />
</section>
<section>
<h2>📅 Available this week</h2>
<p>Starting soon</p>
<ActivityGrid activities={activities.availableSoon} />
</section>
</div>
);
};
Step 3: Personalization Engine (Backend)
# services/personalization.py
def get_personalized_activities(child_age, location, interests):
"""
Returns personalized activity recommendations
"""
# Filter by age appropriateness
age_filtered = Activity.query.filter(
Activity.min_age <= child_age,
Activity.max_age >= child_age
)
# Filter by location (within 10km radius)
location_filtered = age_filtered.filter(
geo_distance(Activity.location, location) <= 10
)
# Score by relevance
activities = []
for activity in location_filtered:
score = calculate_relevance_score(activity, interests, child_age)
activities.append((activity, score))
# Sort by score
activities.sort(key=lambda x: x[1], reverse=True)
return {
'recommended': activities[:12], # Top 12
'trending': get_trending(location, child_age)[:12],
'availableSoon': get_starting_soon(location, child_age)[:12]
}
def calculate_relevance_score(activity, interests, child_age):
"""
Scoring algorithm for activity relevance
"""
score = 0
# Interest match (highest weight)
if activity.category in interests:
score += 100
# Age appropriateness (optimal age = higher score)
optimal_age = (activity.min_age + activity.max_age) / 2
age_diff = abs(optimal_age - child_age)
score += max(0, 50 - (age_diff * 5)) # Penalty for age mismatch
# Quality signals
score += activity.avg_rating * 10 # 4.5 rating = 45 points
score += min(activity.review_count / 10, 30) # Up to 30 points for reviews
# Availability
if activity.has_openings_this_week:
score += 20
# Provider quality
if activity.provider.is_verified:
score += 15
return score
Tasks:
- Design onboarding flow UI/UX
- Implement OnboardingModal component
- Build personalization engine (backend)
- Create PersonalizedResults page
- Add localStorage persistence (remember child info)
- Implement "Add another child" functionality
- Analytics tracking (onboarding completion rate)
- A/B test: Show onboarding to 50% of new visitors
Acceptance Criteria:
- Onboarding flow < 30 seconds to complete
- 70%+ completion rate
- Personalized results feel relevant (user survey)
- Child info persists across sessions
- Multi-child support works
- Analytics tracking operational
Owner: Product Manager + Frontend + Backend Developers Timeline: Week 1-2 of Month 1
Expected Impact:
- 50% improvement in search-to-view rate
- 30% reduction in time-to-book
- 25% increase in conversion rate
P1-2: Implement Trust & Safety Badges
Priority: P0 Effort: 24 hours Impact: High
Objective: Build comprehensive trust signal system
Components:
1. Provider Verification Badge System
// components/ProviderBadges.tsx
const BADGE_TYPES = {
BACKGROUND_CHECK: {
icon: '✓',
label: 'Background Checked',
description: 'Passed comprehensive background check',
color: 'green'
},
INSURANCE_VERIFIED: {
icon: '🛡️',
label: 'Insured',
description: 'Carries liability insurance',
color: 'blue'
},
IDENTITY_VERIFIED: {
icon: '👤',
label: 'ID Verified',
description: 'Government-issued ID confirmed',
color: 'purple'
},
REFERENCE_CHECKED: {
icon: '📋',
label: 'References Verified',
description: 'Professional references confirmed',
color: 'orange'
},
FIRST_AID_CERTIFIED: {
icon: '➕',
label: 'First Aid Certified',
description: 'Current first aid certification',
color: 'red'
},
HIGHLY_RATED: {
icon: '