Loading...
Loading...
Viral growth infrastructure. Social sharing, OG images, referral systems, distribution. Make your product inherently shareable. Audit, implement, verify—every time.
npx skill4agent add phrazzld/claude-config viralitymastermaingit checkout -b feat/virality-$(date +%Y%m%d)# OG tags present?
grep -rE "og:title|og:description|og:image" \
--include="*.tsx" --include="*.ts" --include="*.html" \
app/ src/ pages/ 2>/dev/null | head -5
# Twitter cards?
grep -rE "twitter:card|twitter:title|twitter:image" \
--include="*.tsx" --include="*.ts" \
app/ src/ pages/ 2>/dev/null | head -5
# Dynamic OG images?
[ -f "app/api/og/route.tsx" ] || [ -d "pages/api/og" ] && echo "✓ OG image endpoint" || echo "✗ OG image endpoint"
# Metadata in layout?
grep -q "generateMetadata\|metadata" app/layout.tsx 2>/dev/null && echo "✓ Root metadata" || echo "✗ Root metadata"# Share buttons exist?
grep -rE "share|Share|navigator.share|clipboard" \
--include="*.tsx" --include="*.ts" \
components/ src/ 2>/dev/null | head -5
# Shareable URLs?
grep -rE "shareUrl|shareLink|getShareUrl" \
--include="*.tsx" --include="*.ts" \
. 2>/dev/null | grep -v node_modules | head -5
# Deep linking?
grep -rE "searchParams|useSearchParams|\[.*\]" \
--include="*.tsx" --include="*.ts" \
app/ pages/ 2>/dev/null | head -5# Referral codes?
grep -rE "referral|invite|inviteCode|refCode" \
--include="*.tsx" --include="*.ts" --include="*.sql" \
. 2>/dev/null | grep -v node_modules | head -5
# Attribution tracking?
grep -rE "utm_|referrer|attribution" \
--include="*.tsx" --include="*.ts" \
. 2>/dev/null | grep -v node_modules | head -5# Product Hunt assets?
[ -f "public/product-hunt-logo.png" ] || [ -d "public/launch" ] && echo "✓ Launch assets" || echo "✗ Launch assets"
# Press kit / media assets?
[ -d "public/press" ] || [ -d "public/media" ] && echo "✓ Press kit" || echo "✗ Press kit"
# Changelog / updates page?
[ -f "app/changelog/page.tsx" ] || [ -f "pages/changelog.tsx" ] && echo "✓ Changelog page" || echo "✗ Changelog page"// app/layout.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
metadataBase: new URL(process.env.NEXT_PUBLIC_APP_URL!),
title: {
default: 'Your Product - Tagline',
template: '%s | Your Product',
},
description: 'One sentence that makes people want to try it.',
openGraph: {
type: 'website',
locale: 'en_US',
siteName: 'Your Product',
images: ['/og-default.png'],
},
twitter: {
card: 'summary_large_image',
creator: '@yourhandle',
},
};// app/api/og/route.tsx
import { ImageResponse } from 'next/og';
export const runtime = 'edge';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const title = searchParams.get('title') ?? 'Your Product';
const description = searchParams.get('description') ?? '';
return new ImageResponse(
(
<div
style={{
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#000',
color: '#fff',
fontFamily: 'system-ui',
}}
>
<div style={{ fontSize: 60, fontWeight: 'bold' }}>{title}</div>
{description && (
<div style={{ fontSize: 30, marginTop: 20, opacity: 0.8 }}>
{description}
</div>
)}
</div>
),
{ width: 1200, height: 630 }
);
}// app/[slug]/page.tsx
import type { Metadata } from 'next';
export async function generateMetadata({ params }): Promise<Metadata> {
const item = await getItem(params.slug);
return {
title: item.title,
description: item.description,
openGraph: {
title: item.title,
description: item.description,
images: [`/api/og?title=${encodeURIComponent(item.title)}`],
},
};
}// components/share-button.tsx
'use client';
import { useState } from 'react';
interface ShareButtonProps {
url: string;
title: string;
text?: string;
}
export function ShareButton({ url, title, text }: ShareButtonProps) {
const [copied, setCopied] = useState(false);
const share = async () => {
// Try native share first (mobile)
if (navigator.share) {
try {
await navigator.share({ url, title, text });
return;
} catch {
// User cancelled or not supported
}
}
// Fallback to clipboard
await navigator.clipboard.writeText(url);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<button onClick={share}>
{copied ? 'Copied!' : 'Share'}
</button>
);
}// After user completes an action
function onComplete() {
// Show share prompt
showShareModal({
title: "You did the thing!",
prompt: "Share your achievement?",
url: `${baseUrl}/share/${resultId}`,
prefilledText: "I just [did thing] with @YourProduct!",
});
}// lib/referrals.ts
export function generateReferralCode(userId: string): string {
// Short, memorable codes
return `${userId.slice(0, 4).toUpperCase()}${randomChars(4)}`;
}
export function getReferralUrl(code: string): string {
return `${process.env.NEXT_PUBLIC_APP_URL}?ref=${code}`;
}
// Track on signup
export async function trackReferral(newUserId: string, refCode: string | null) {
if (!refCode) return;
const referrer = await getUserByRefCode(refCode);
if (!referrer) return;
await db.referrals.create({
referrerId: referrer.id,
referredId: newUserId,
code: refCode,
createdAt: new Date(),
});
// Reward referrer (if applicable)
await grantReferralReward(referrer.id);
}// middleware.ts or on page load
export function captureAttribution() {
if (typeof window === 'undefined') return;
const params = new URLSearchParams(window.location.search);
const attribution = {
utm_source: params.get('utm_source'),
utm_medium: params.get('utm_medium'),
utm_campaign: params.get('utm_campaign'),
ref: params.get('ref'),
referrer: document.referrer,
};
// Store for later (signup, conversion)
sessionStorage.setItem('attribution', JSON.stringify(attribution));
}// K-factor = invites sent per user × conversion rate
// K > 1 means viral growth
export async function calculateKFactor(timeWindow: string = '30d') {
const activeUsers = await countActiveUsers(timeWindow);
const invitesSent = await countInvitesSent(timeWindow);
const inviteConversions = await countInviteConversions(timeWindow);
const invitesPerUser = invitesSent / activeUsers;
const conversionRate = inviteConversions / invitesSent;
const kFactor = invitesPerUser * conversionRate;
return {
kFactor,
invitesPerUser,
conversionRate,
isViral: kFactor > 1,
};
}# Use a validator
open "https://www.opengraph.xyz/url/$(echo $YOUR_URL | jq -sRr @uri)"
# Or curl and check
curl -s "$YOUR_URL" | grep -E "og:|twitter:" | head -10# Check the endpoint works
curl -I "https://yoursite.com/api/og?title=Test"
# Should return image/png content-typeUser creates something → Prompted to share → Friend sees → Creates their own → ...User hits milestone → Shareable achievement card → Social proof → Friend tries → ...User invites friend → Friend joins → Both rewarded → Friend invites → ...User creates content → Content has product watermark → Viewer sees → Visits product → ...# Validate OG tags
npx open-graph-scraper https://yoursite.com
# Generate social images locally
npx @vercel/og-image "Title" --output og.png
# Check Twitter card
open "https://cards-dev.twitter.com/validator"launch-strategysocial-contentmarketing-ideas