Customize Marketing Pages in the Tanstack Start Supabase Turbo Starter Kit
Build and customize landing pages, pricing pages, FAQ, and other marketing content using Tanstack Start App Router and Tailwind CSS.
Marketing pages in Makerkit live at apps/web/src/routes/_marketing/ and include landing pages, pricing, FAQ, blog, changelog, and contact forms. These pages use TanStack Router file-based routing for fast initial loads and SEO optimization.
Marketing Pages Development
Customize and extend your marketing pages
Marketing Pages Structure
The marketing pages follow TanStack Router file-based conventions. The _marketing prefix is a pathless layout route — it wraps its children with a shared header/footer layout without adding a URL segment:
apps/web/src/routes/_marketing/├── route.tsx # Shared layout with header/footer├── index.tsx # Home page (/)├── cookie-policy.tsx # Legal pages├── privacy-policy.tsx├── terms-of-service.tsx├── contact.tsx # Contact form├── faq.tsx # FAQ page├── pricing.tsx # Pricing page├── blog/ # Blog listing and posts│ ├── index.tsx│ └── $slug.tsx└── changelog/ # Product changelog ├── index.tsx └── $slug.tsxShared marketing components (header, footer, navigation) live in apps/web/src/components/marketing/.
Pathless Layout Routes Explained
The _marketing folder is a pathless layout route: the leading underscore means it shares route.tsx's layout without contributing a URL segment. Pages inside render at the root level:
| File Path | URL |
|---|---|
_marketing/index.tsx | / |
_marketing/pricing.tsx | /pricing |
_marketing/blog/index.tsx | /blog |
Customizing Existing Pages
Home Page
The home page at apps/web/src/routes/_marketing/index.tsx typically includes:
apps/web/src/routes/_marketing/index.tsx
import { createFileRoute } from '@tanstack/react-router';import { Hero } from '#/components/marketing/hero.tsx';import { Features } from '#/components/marketing/features.tsx';import { Testimonials } from '#/components/marketing/testimonials.tsx';import { Pricing } from '#/components/marketing/pricing-section.tsx';import { CallToAction } from '#/components/marketing/call-to-action.tsx';export const Route = createFileRoute('/_marketing/')({ component: Home,});function Home() { return ( <> <Hero /> <Features /> <Testimonials /> <Pricing /> <CallToAction /> </> );}Each section is a separate component in components/marketing/ for easy customization.
Pricing Page
The pricing page displays your billing plans. It reads configuration from apps/web/src/config/billing.config.ts:
apps/web/src/routes/_marketing/pricing.tsx
import { createFileRoute } from '@tanstack/react-router';import { PricingTable } from '@kit/billing-gateway/marketing';import billingConfig from '#/config/billing.config.ts';import pathsConfig from '#/config/paths.config.ts';const paths = { signUp: pathsConfig.auth.signUp, return: pathsConfig.app.home,};export const Route = createFileRoute('/_marketing/pricing')({ component: PricingPage,});function PricingPage() { return ( <div className="container mx-auto py-16"> <h1 className="text-4xl font-bold text-center mb-4"> Simple, Transparent Pricing </h1> <p className="text-muted-foreground text-center mb-12"> Choose the plan that fits your needs </p> <PricingTable config={billingConfig} paths={paths} /> </div> );}See Billing Configuration for customizing plans and pricing.
FAQ Page
The FAQ page uses an accordion component with content from a configuration file or CMS:
apps/web/src/routes/_marketing/faq.tsx
import { createFileRoute } from '@tanstack/react-router';import { Accordion, AccordionContent, AccordionItem, AccordionTrigger,} from '@kit/ui/accordion';const faqs = [ { question: 'How do I get started?', answer: 'Sign up for a free account and follow our getting started guide.', }, { question: 'Can I cancel anytime?', answer: 'Yes, you can cancel your subscription at any time with no penalties.', }, // ... more FAQs];export const Route = createFileRoute('/_marketing/faq')({ component: FAQPage,});function FAQPage() { return ( <div className="container mx-auto max-w-3xl py-16"> <h1 className="text-4xl font-bold text-center mb-12"> Frequently Asked Questions </h1> <Accordion type="single" collapsible> {faqs.map((faq, index) => ( <AccordionItem key={index} value={`item-${index}`}> <AccordionTrigger>{faq.question}</AccordionTrigger> <AccordionContent>{faq.answer}</AccordionContent> </AccordionItem> ))} </Accordion> </div> );}Contact Page
The contact page includes a form that sends emails via your configured mailer:
apps/web/src/routes/_marketing/contact.tsx
import { createFileRoute } from '@tanstack/react-router';import { ContactForm } from '#/components/marketing/contact-form.tsx';export const Route = createFileRoute('/_marketing/contact')({ component: ContactPage,});function ContactPage() { return ( <div className="container mx-auto max-w-xl py-16"> <h1 className="text-4xl font-bold text-center mb-4"> Contact Us </h1> <p className="text-muted-foreground text-center mb-8"> Have a question? We'd love to hear from you. </p> <ContactForm /> </div> );}Contact Form Configuration
Configure the recipient email address in your environment:
.env.local
CONTACT_EMAIL=support@yourdomain.comThe form submission uses your email configuration. Ensure your mailer is configured before the contact form will work.
Creating New Marketing Pages
Basic Page Structure
Create a route file. Add a new .tsx file under apps/web/src/routes/_marketing/ and set metadata with the route head option:
apps/web/src/routes/_marketing/about.tsx
import { createFileRoute } from '@tanstack/react-router';export const Route = createFileRoute('/_marketing/about')({ head: () => ({ meta: [ { title: 'About Us | Your SaaS Name' }, { name: 'description', content: 'Learn about our mission, team, and the story behind our product.', }, ], }), component: AboutPage,});function AboutPage() { return ( <div className="container mx-auto py-16"> <h1 className="text-4xl font-bold mb-8">About Us</h1> <div className="prose prose-gray max-w-none"> <p>Your company story goes here...</p> </div> </div> );}After adding a route file, regenerate the route tree:
pnpm --filter web generate-routesContent-Heavy Pages
For long-form content that non-developers should be able to edit, source the content from the CMS instead of hardcoding it (see the "Dynamic Pages with Data" example below and the CMS documentation).
Dynamic Pages with Data
For pages that need dynamic data, fetch it in the route loader and render it in the component:
apps/web/src/routes/_marketing/customers.tsx
import { createFileRoute } from '@tanstack/react-router';import { createCmsClient } from '@kit/cms';export const Route = createFileRoute('/_marketing/customers')({ loader: async () => { const cms = await createCmsClient(); const caseStudies = await cms.getContentItems({ collection: 'case-studies', limit: 10, }); return { caseStudies }; }, component: CustomersPage,});function CustomersPage() { const { caseStudies } = Route.useLoaderData(); return ( <div className="container mx-auto py-16"> <h1 className="text-4xl font-bold mb-12">Customer Stories</h1> <div className="grid md:grid-cols-2 gap-8"> {caseStudies.items.map((study) => ( <CaseStudyCard key={study.slug} {...study} /> ))} </div> </div> );}Navigation and Footer
Header Navigation
Configure navigation links in the header component:
apps/web/src/components/marketing/site-navigation.tsx
const navigationItems = [ { label: 'Features', href: '/#features' }, { label: 'Pricing', href: '/pricing' }, { label: 'Blog', href: '/blog' }, { label: 'Docs', href: '/docs' }, { label: 'Contact', href: '/contact' },];Footer Links
The footer typically includes multiple link sections:
apps/web/src/components/marketing/site-footer.tsx
const footerSections = [ { title: 'Product', links: [ { label: 'Features', href: '/#features' }, { label: 'Pricing', href: '/pricing' }, { label: 'Changelog', href: '/changelog' }, ], }, { title: 'Resources', links: [ { label: 'Documentation', href: '/docs' }, { label: 'Blog', href: '/blog' }, { label: 'FAQ', href: '/faq' }, ], }, { title: 'Legal', links: [ { label: 'Privacy Policy', href: '/privacy-policy' }, { label: 'Terms of Service', href: '/terms-of-service' }, { label: 'Cookie Policy', href: '/cookie-policy' }, ], },];Customizing the Layout
All marketing pages inherit from the pathless layout route at apps/web/src/routes/_marketing/route.tsx. This layout includes:
- Header with navigation
- Footer with links
- Shared structure via
<Outlet />
Edit this file to change the shared structure across all marketing pages.
SEO for Marketing Pages
Route head option
Set SEO metadata with the route head option:
apps/web/src/routes/_marketing/pricing.tsx
export const Route = createFileRoute('/_marketing/pricing')({ head: () => ({ meta: [ { title: 'Pricing | Your SaaS Name' }, { name: 'description', content: 'Choose from flexible pricing plans. Start free, upgrade when ready.', }, { property: 'og:title', content: 'Pricing | Your SaaS Name' }, { property: 'og:description', content: 'Choose from flexible pricing plans.', }, { property: 'og:image', content: '/images/og/pricing.png' }, ], }), component: PricingPage,});Structured Data
Add JSON-LD structured data for rich search results by rendering a <script type="application/ld+json"> tag inside the route component:
function PricingPage() { return ( <> <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify({ '@context': 'https://schema.org', '@type': 'Product', name: 'Your SaaS Name', offers: { '@type': 'AggregateOffer', lowPrice: '0', highPrice: '99', priceCurrency: 'USD', }, }), }} /> {/* Page content */} </> );}Sitemap
Add new marketing pages to your sitemap by extending the STATIC_PATHS array in apps/web/src/routes/sitemap[.]xml.ts:
const STATIC_PATHS = [ '/', '/pricing', '/faq', '/blog', '/docs', '/contact', '/about', // Add new pages here];Related Resources
- SEO Configuration for detailed SEO setup
- Legal Pages for privacy policy and terms
- CMS Setup for blog configuration
- Email Configuration for contact form setup