Marketing & SEO
Assemble the prerendered landing surface and emit structured data, canonical and hreflang links, sitemap, and robots from config.
The marketing surface is driven by @repo/config - the config.seo block, the config.indexable master switch, and config.business / config.social for structured data. Every SEO artifact is derived from those keys, so config is the single source of truth.
Landing & marketing pages
The landing page (app/[locale]/(marketing)/page.tsx) composes section components from components/landing/ in this order: Hero, StatsRow, DemoVideo, Showcase, FeaturesListicle, HowItWorks, ProblemSolution, TestimonialsGrid, Pricing, PricingComparison, PricingProducts, FAQ, FinalCta, plus a FaqJsonLd node.
| Route | Purpose |
|---|---|
/ | Landing page, pricing included at the #pricing anchor |
/contact | Contact form |
/terms, /privacy, /cookies, /refund | Legal pages |
There is no separate pricing page. Plans render from pricingConfig.plans inside the landing page's #pricing section, and next.config.mjs permanently redirects /pricing to /#pricing. Link to plans with config.routes.pricing, never a literal path.
Editing landing copy
All visible copy is i18n - there is no hardcoded marketing text.
- Section copy lives under
landing.*keys and the landing<title>/ meta description underhome.meta, both inpackages/i18n/translations/en/web.json. Only editen; other locales are generated. - Hero avatars are the one shared data export,
socialProofAvatarsinpackages/config/src/landing.ts- five files self-hosted underpublic/avatars/. Replace the array and the.webpfiles when rebranding. - Section-aware footer and navbar: an item carrying a
sectiondrops out when that section has no content for the locale - see Content sections. - Footer attribution:
config.builtWithrenders "Built by{founderName}using GenerateSaaS". ClearfounderNamefor the shorter line, or delete the block - see configuration.
Structured data
Every node is emitted with the <JsonLd> component (React-19-safe inline application/ld+json), cross-linked by @id into one graph.
| JSON-LD node | Emitted on | Built from |
|---|---|---|
Organization + WebSite | every page | config.business, config.social, config.seo, config.logo |
WebPage (+ BreadcrumbList) | WebPage on every page; a BreadcrumbList joins it on content pages, contact, legal and /download, each of which also renders the trail on screen | page title + description |
| Content-page graph | every content document, listing and hub | SEO kit |
FAQPage | / | faqItems (i18n) |
SoftwareApplication (+ AggregateRating + Review when opted in) | / | pricingConfig + lib/landing/testimonials.ts |
| Source key | Feeds |
|---|---|
config.business?.name (into config.siteName) | JSON-LD name / legalName - a placeholder, edit before launch |
config.baseUrl | JSON-LD url, sitemap entries |
config.logo?.main | JSON-LD logo |
config.seo?.foundingDate | JSON-LD foundingDate (optional) |
config.social | JSON-LD sameAs array; twitter:site derives from config.social?.x |
config.seo.description | Organization JSON-LD description + the RSS channel of every article section, not the page <meta description> |
AggregateRating and Review are off unless you set config.seo.productRating: true. They are built from lib/landing/testimonials.ts, which ships placeholder reviews - Google ignores reviews a business writes about itself and publishing them risks a manual action. Turn the option on only once the ratings are real.
The indexable switch
config.indexable is the master switch behind app/robots.txt/route.ts and app/sitemap.ts.
| State | robots.txt | sitemap |
|---|---|---|
true (production) | Allow: /, the AI crawler groups, plus a sitemap pointer | One entry per route and locale |
false (demos, staging) | Disallow: / | Empty |
The named AI crawler groups and Content Signals in that file come from config.agents.crawlers - see AI Agents & Crawlers.
Dashboard, admin, auth, every section's /search, tag archives, the ?sort= variants of an article archive, and /unsubscribe carry a per-route noindex meta tag (Metadata.robots) rather than a robots.txt Disallow, so crawlers read the directive and drop the page cleanly. A section with content puts its pages in the sitemap, adds an RSS <link rel="alternate"> to <head> when it is an article section, and the first article section gives the WebSite node its SearchAction.
Waitlist
Turn the landing surface into a pre-launch capture page.
Content sections
Blog, alternatives and compare pages that feed the sitemap, RSS, and SearchAction.
SEO kit
Metadata, breadcrumbs, JSON-LD, and OG images for content pages.
Plans & Pricing
The pricingConfig data behind the marketing pricing sections.
Configuration
Every config flag and key referenced on this page.
Banners
Ship announcement bars on the marketing and dashboard surfaces, each toggled by config.banner, with role and plan targeting plus dismissal on the dashboard.
SEO kit
One shared layer giving every content and marketing page article-grade metadata, canonical and hreflang, breadcrumbs, JSON-LD, generated OG images, markdown renditions, and a required-fields guard.