Skip to Content
TechnicalApplicationsMarketing Application

Marketing Application

The marketing application is the primary public-facing website for the Sustentus platform, built with Next.js 15 and the App Router.

Overview

Package Name: @sustentus/marketing Framework: Next.js 15 Port: 3001 Path: apps/marketing/

Purpose

The marketing site serves as the main entry point for potential customers, featuring:

  • Product information and value proposition
  • Pricing and plan comparison
  • Waitlist signup functionality
  • User authentication (sign-up/sign-in)

Technology Stack

Core

  • Next.js 15: React framework with App Router
  • React 19: UI library
  • TypeScript: Type safety
  • Tailwind CSS v4: Utility-first styling

Authentication

  • Clerk: User authentication and management
  • @clerk/nextjs: Next.js integration
  • @clerk/themes: Pre-built UI themes

UI Components

  • @sustentus/ui: Shared component library
  • Radix UI: Accessible primitives
  • Lucide React: Icons
  • Framer Motion: Animations

Forms & Validation

  • React Hook Form: Form state management
  • Zod: Schema validation
  • @hookform/resolvers: React Hook Form + Zod integration

Additional Features

  • Vercel Analytics: Performance monitoring
  • PostHog: Product analytics
  • Sonner: Toast notifications
  • Next Themes: Theme management

Directory Structure

marketing/ ├── app/ │ ├── layout.tsx # Root layout with providers │ ├── page.tsx # Home/landing page │ ├── globals.css # Global styles │ ├── pricing/ │ │ └── page.tsx # Pricing page │ ├── sign-in/ │ │ └── [[...sign-in]]/ │ │ └── page.tsx # Clerk sign-in page │ ├── sign-up/ │ │ └── [[...sign-up]]/ │ │ └── page.tsx # Clerk sign-up page │ ├── waitlist/ │ │ └── page.tsx # Waitlist signup │ └── waitlist-confirmation/ │ └── page.tsx # Post-signup confirmation ├── components/ │ ├── ui/ # shadcn/ui components │ ├── home/ # Landing page sections │ │ ├── hero.tsx │ │ ├── problem.tsx │ │ ├── solution.tsx │ │ ├── features.tsx │ │ ├── how-it-works.tsx │ │ ├── testimonials.tsx │ │ └── cta.tsx │ ├── pricing/ │ │ └── pricing-cards.tsx │ ├── background/ │ │ └── grid-background.tsx │ ├── navigation.tsx # Global navigation │ ├── theme-provider.tsx # Theme context │ ├── auth-provider.tsx # Clerk provider │ └── waitlist.tsx # Waitlist form ├── lib/ │ └── utils.ts # Utility functions ├── public/ # Static assets ├── middleware.ts # Clerk middleware ├── next.config.mjs # Next.js configuration └── package.json

Key features

  • Landing page (app/page.tsx): hero, problem, solution, feature highlights, how it works, testimonials, and call-to-action sections.
  • Pricing page (app/pricing/page.tsx): plan cards, monthly/annual toggle, and signup CTAs.
  • Authentication (Clerk): /sign-up for registration, /sign-in for login; users are redirected to /waitlist-confirmation after auth.
  • Waitlist: validated signup form that creates a Clerk user and shows a confirmation page.

Development

Running the App

# From root pnpm marketing:dev # From marketing directory cd apps/marketing pnpm dev

The app runs on http://localhost:3001.

Environment Variables

Create .env.local in apps/marketing/:

# Clerk Authentication NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... # Optional: Analytics NEXT_PUBLIC_POSTHOG_KEY=... NEXT_PUBLIC_POSTHOG_HOST=...

Building

# From root pnpm marketing:build # From marketing directory cd apps/marketing pnpm build

Configuration Files

  • next.config.mjs: image domains, redirects, headers, experimental features.
  • middleware.ts: Clerk middleware — protects authenticated routes, configures public routes, and handles post-auth redirects.
  • components.json: shadcn/ui configuration (style, Tailwind config path, aliases).

Routing

The app uses Next.js App Router with file-based routing:

RoutePurpose
/Landing page
/pricingPricing information
/sign-inUser login
/sign-upUser registration
/waitlistWaitlist signup
/waitlist-confirmationPost-signup confirmation

Forms

Forms use React Hook Form with a Zod resolver and the <Form> primitives from @sustentus/ui:

const schema = z.object({ email: z.string().email(), // ... more fields }); const form = useForm({ resolver: zodResolver(schema), }); const onSubmit = async (data) => { // Handle submission };

Learn More

Last updated on