senternet-site-ads-landing
Create conversion-focused campaign landing pages for paid ads.
SKILL.md.claude/skills/senternet-site-ads-landing/SKILL.md
GitHubAd Landing Pages
Create conversion-optimized landing pages for paid ad campaigns (Reddit, Google, Meta) with campaign-specific messaging and tracking.
Architecture
Landing pages live at /landing/CAMPAIGN-NAME routes. Each is a stripped-down, conversion-focused version of the site with:
- No site navigation (reduces distraction, keeps users on page)
- Campaign-specific headline and body copy matched to the ad creative
- Single prominent CTA (App Store / website link)
- Reddit Pixel conversion tracking on CTA click
- No external links except the primary CTA
- Prerendered like all other routes
Steps
1. Create a generic LandingPage component
// src/components/LandingPage.tsx
import { MetaTags } from './MetaTags';
import { trackRedditEvent } from './RedditPixel';
interface LandingPageProps {
headline: string;
body: string;
ctaText: string;
ctaUrl: string;
badge?: string; // e.g. "Featured on BetaList"
metaTitle: string;
metaDescription: string;
shareImage?: string;
}
export function LandingPage({ headline, body, ctaText, ctaUrl, badge, metaTitle, metaDescription, shareImage }: LandingPageProps) {
return (
<>
<MetaTags
title={metaTitle}
description={metaDescription}
image={shareImage}
/>
<main className="min-h-screen bg-[#0a0a0f] flex flex-col items-center justify-center px-6 text-center">
{badge && (
<span className="mb-6 text-sm font-medium text-cyan-400 border border-cyan-400/30 rounded-full px-4 py-1">
{badge}
</span>
)}
<h1 className="text-4xl md:text-6xl font-bold text-white max-w-2xl mb-6">
{headline}
</h1>
<p className="text-xl text-gray-400 max-w-xl mb-10">
{body}
</p>
<a
href={ctaUrl}
onClick={() => trackRedditEvent('Lead')}
className="bg-cyan-400 text-black font-bold px-8 py-4 rounded-full text-lg hover:bg-cyan-300 transition-colors"
>
{ctaText}
</a>
</main>
</>
);
}
2. Create campaign-specific landing page routes
// src/components/LandingPages.tsx
import { LandingPage } from './LandingPage';
export function RedditCampaignLanding() {
return (
<LandingPage
headline="[Your Core Value Proposition]"
body="APPNAME helps you [key benefit]. No account needed."
ctaText="Try It Free"
ctaUrl="https://www.DOMAIN.com/signup"
metaTitle="APPNAME - [Short Value Prop]"
metaDescription="[150-char description of what APPNAME does and why to try it]"
/>
);
}
export function RetargetingLanding() {
return (
<LandingPage
headline="[Objection-handling or urgency headline]"
body="[Reinforce the benefit. Address the hesitation from the first visit.]"
ctaText="Get Started"
ctaUrl="https://www.DOMAIN.com/signup"
metaTitle="APPNAME - [Campaign-Specific Title]"
metaDescription="[Alternate description targeting this audience segment]"
/>
);
}
3. Add landing routes to src/App.tsx
<Route path="/landing/reddit" element={<RedditCampaignLanding />} />
<Route path="/landing/retargeting" element={<RetargetingLanding />} />
<Route path="/landing/betalist" element={<BetaListLanding />} />
4. Add landing pages to scripts/prerender.mjs
const ROUTES = [
'/',
// ... other routes
'/landing/reddit',
'/landing/retargeting',
'/landing/betalist',
];
5. Add landing pages to public/sitemap.xml
Consider whether ad landing pages should be indexed by Google. Options:
- Include in sitemap (Google can find and rank them organically)
- Exclude from sitemap + add
<MetaTags robots="noindex">(keeps them purely for paid traffic)
Typically: index them — organic search for long-tail queries is free traffic.
6. Use campaign-specific UTM params in the CTA URL
const ctaUrl = "https://apps.apple.com/app/...&pt=CAMPAIGN_ID&ct=reddit_pro_landing&mt=8";
For web-to-web redirects (not App Store), append UTM params:
https://www.DOMAIN.com?utm_source=reddit&utm_medium=cpc&utm_campaign=reddit-pro
7. Create ad creative assets
For Reddit campaigns, create HTML ad assets in ads/:
ads/
├── banner.html # 728x90 leaderboard
├── reddit.html # Reddit-specific feed ad format (300x250)
├── story.html # Reddit story ad (1080x1920)
├── square.html # Square format (1200x1200)
└── capture.js # Puppeteer script to screenshot HTML → PNG
The capture.js script uses Puppeteer to screenshot each HTML file at the correct dimensions:
import puppeteer from 'puppeteer';
const ADS = [
{ file: 'reddit.html', width: 300, height: 250, name: 'reddit-300x250' },
{ file: 'banner.html', width: 728, height: 90, name: 'banner-728x90' },
{ file: 'square.html', width: 1200, height: 1200, name: 'square-1200x1200' },
{ file: 'story.html', width: 1080, height: 1920, name: 'story-1080x1920' },
];
const browser = await puppeteer.launch();
for (const ad of ADS) {
const page = await browser.newPage();
await page.setViewport({ width: ad.width, height: ad.height });
await page.goto(`file://${process.cwd()}/ads/${ad.file}`);
await page.screenshot({ path: `ads/out/${ad.name}.png` });
await page.close();
}
await browser.close();
Checklist for each new landing page
- Route added to
src/App.tsx - Route added to
scripts/prerender.mjsROUTES array -
<MetaTags>called with page-specific title and description - Reddit pixel
Leadevent fires on CTA click - No site navigation (stripped-down layout)
- Single, prominent CTA
- Mobile-first design tested at 375px width
- Page loads fast on mobile (no heavy images above fold)
Framework: Next.js track
- The shared
LandingPagecomponent lives incomponents/; each campaign isapp/lp/<campaign>/page.tsx. - Campaign pages are almost always
noindex: setrobots: { index: false, follow: true }in the page'smetadataand mark the routeindexable: falseinconfig/routes.mjsin the same change, so it never reaches the sitemap or IndexNow. - Keep the page a server component. The only parts that need
'use client'are the CTA click tracking and any form. - UTM parameters read via
useSearchParams()must sit inside a<Suspense>boundary in a client component, or the whole page opts into dynamic rendering — which turns a static landing page into a cold start on the exact traffic you are paying for. - Conversion pixels are wired as described in
/senternet-site-ads-reddit-pixelon this track.
See /senternet-site-framework for the full convention map.