Centralized Landing Content & CSP Dev Fix
One File to Rebrand the Entire Landing Page
All landing page copy now lives in a single file: content/landing.ts. Hero taglines, feature descriptions, testimonials, FAQ items, CTA text, and footer links - everything a buyer needs to change before shipping is in one place.
Before
Copy was scattered across 7 component files (Hero.tsx, Features.tsx, Testimonials.tsx, Pricing.tsx, FAQ.tsx, CTA.tsx, Footer.tsx) in inline const blocks and hardcoded JSX strings. Rebranding meant hunting through every component.
After
Components are now pure presentation. They import from content/landing.ts and render whatever's there. To rebrand:
- Edit
content/landing.ts(all marketing copy) - Edit
config.ts(app name, pricing plans, SEO defaults) - Done. Zero component edits needed.
Type-safe Icon Picker
Feature icons are referenced by string key (e.g., icon: 'lock') instead of requiring lucide-react imports. A shared _icons.ts map ships with 20 common SaaS icons and provides autocomplete via the IconKey type. Lucide is tree-shaken, so unused icons cost zero bundle bytes.
Available icons: lock, credit-card, mail, database, shield, zap, sparkles, rocket, users, bar-chart, globe, bell, message-square, calendar, file-text, cloud, settings, heart, star, check-circle.
Self-Invalidating Demo Content
The placeholder copy is written to look professional but talks about the template itself ("This template saved me weeks of work", "Fork it, rebrand it, ship it"). This means:
- First clone looks polished, not like a skeleton
- Buyers can't accidentally ship placeholder copy because it obviously doesn't describe their product
- A warning comment at the top of
content/landing.tsreminds buyers to replace everything before production
CSP Fix for Development Mode
React 19's development build requires dynamic code execution for Fast Refresh and stack reconstruction, which the Content Security Policy header was blocking. The CSP now conditionally relaxes script-src only when NODE_ENV=development. Production CSP is unchanged and remains strict.
What Changed
content/landing.tsadded (all landing copy, typed)components/landing/_icons.tsadded (20-icon map withIconKeytype)components/landing/Hero.tsxupdated (reads from content)components/landing/Features.tsxupdated (reads from content, uses icon map)components/landing/Testimonials.tsxupdated (reads from content)components/landing/Pricing.tsxupdated (6 hardcoded strings extracted)components/landing/FAQ.tsxupdated (reads from content)components/landing/CTA.tsxupdated (reads from content)components/landing/Footer.tsxupdated (reads from content)components/landing/faq-data.tsdeleted (absorbed into content/landing.ts)lib/seo.tsupdated (FAQ import path)next.config.tsupdated (dev-only CSP relaxation)