Skip to main content

🎨 Marketing Landing Page (apps/web)

Shipnative includes a ready-to-use marketing landing page built with Vite + React + Tailwind CSS. This is perfect for collecting emails before launch or showcasing your app after it’s live.
Not sure which landing page this is?
  • apps/web/ - Marketing page included with your app (this guide)
  • landing_page/ - Separate repo for Shipnative’s own marketing site

✨ Two Modes

The marketing page has two modes you can switch between:

πŸ“§ Waitlist Mode (Pre-Launch)

Perfect for collecting early user emails before your app launches. Features:
  • Email capture form with validation
  • Success confirmation message
  • App screenshot showcase
  • Feature bento grid with 6 tiles:
    • AI-first architecture
    • Mock mode
    • Premium design system
    • Monetization
    • Analytics & reliability
    • Optimized dev loop
  • AI tools integration section (Cursor, Claude, Copilot, etc.)
  • Fully responsive design
  • SEO optimized
When to use: Building in public, collecting beta testers, pre-launch hype

πŸš€ Launch Mode (Post-Launch)

Perfect for when your app is live on the App Store and Google Play. Features:
  • App Store and Google Play badge links
  • Hero section with app name and tagline
  • Feature highlights (3 key benefits)
  • Clean, minimal design focused on downloads
  • SEO optimized
When to use: App is live and you want to drive downloads

πŸ› οΈ Quick Setup

yarn setup now also asks for the marketing page mode and your Resend API key. It writes apps/web/.env for you, so you can skip to Step 3 if you just ran it.

Step 1: Copy Environment Variables (if you skipped yarn setup)

cp apps/web/.env.example apps/web/.env

Step 2: Configure Your Settings

Edit apps/web/.env (or let yarn setup fill it):
# Choose mode: 'waitlist' or 'launch'
VITE_MODE=waitlist

# Your app details
VITE_APP_NAME=YourApp
VITE_APP_DESCRIPTION=Your app description goes here
VITE_APP_URL=https://yourapp.com

# SEO & Social
VITE_OG_IMAGE=https://yourapp.com/og-image.jpg

# Waitlist Mode Settings
VITE_APP_SCREENSHOT_URL=/app-screenshot.png
VITE_WAITLIST_API_ENDPOINT=https://your-api.com/waitlist  # Optional
RESEND_API_KEY=  # Optional - used by your waitlist backend

# Launch Mode Settings (required only in launch mode)
VITE_IOS_APP_URL=https://apps.apple.com/app/your-app
VITE_ANDROID_APP_URL=https://play.google.com/store/apps/details?id=com.yourapp

Step 3: Switch Modes Quickly

yarn marketing:waitlist   # sets VITE_MODE=waitlist in apps/web/.env
yarn marketing:launch     # sets VITE_MODE=launch in apps/web/.env
# or: yarn marketing:mode <waitlist|launch>

Step 4: Run the Development Server

yarn web        # alias for `yarn marketing:dev`
# or: yarn marketing:dev
Visit http://localhost:5173 to see your marketing page!

🎨 Customization

Switching Between Modes

Use the helper command (it updates apps/web/.env for you):
yarn marketing:mode waitlist   # Pre-launch waitlist
yarn marketing:mode launch     # Post-launch store links
# Aliases: yarn marketing:waitlist / yarn marketing:launch
Prefer to edit manually? Change VITE_MODE in apps/web/.env. No code changes needed - the app automatically renders the correct mode!

Customizing Waitlist Mode

Edit /apps/web/src/components/WaitlistMode.tsx: Change feature tiles (lines 92-164):
const bentoTiles: BentoTile[] = [
  {
    title: 'Your Feature Title',
    description: 'Feature description here',
    icon: '🎨',
    pill: 'Optional badge',
    accent: 'from-[#E0F2FE] via-white to-[#C7E9FF]',  // Gradient colors
    span: 'lg:col-span-2',  // Optional: make tile wider
    points: [
      'Key benefit 1',
      'Key benefit 2',
    ],
    metric: {  // Optional: add a metric
      label: 'to launch',
      value: '< 1 hr',
    },
  },
  // Add more tiles...
]
Change AI tools (lines 33-90):
const aiTools: AiTool[] = [
  {
    name: 'Tool Name',
    url: 'https://tool.com',
    domain: 'tool.com',
    logoText: 'TN',  // 1-2 letter abbreviation
    logoGradient: 'from-[#0C163D] via-[#0F4C75] to-[#2DD4BF]',
    tagline: 'Short description of integration',
  },
  // Add more tools...
]
Add your app screenshot:
  1. Place your screenshot in /apps/web/public/app-screenshot.png
  2. Or update VITE_APP_SCREENSHOT_URL to use a URL

Customizing Launch Mode

Edit /apps/web/src/components/LaunchMode.tsx: Change feature highlights (lines 92-108):
<div className="space-y-2">
  <div className="text-3xl">⚑️</div>
  <h3 className="font-semibold text-gray-900">Your Feature</h3>
  <p className="text-sm text-gray-500">Feature description</p>
</div>
Customize colors and styling: The landing page uses Tailwind CSS. Edit component files to change:
  • Colors: bg-blue-600, text-gray-900, etc.
  • Spacing: p-4, m-8, gap-6, etc.
  • Layout: Grid, flex, responsive breakpoints

Adding a Waitlist API

By default, the waitlist form shows a success message without actually submitting anywhere. To integrate with your email service: Option 1: Use Supabase
// In WaitlistMode.tsx, update handleSubmit (line 192-204)
const apiEndpoint = import.meta.env.VITE_WAITLIST_API_ENDPOINT

if (apiEndpoint) {
  const response = await fetch(apiEndpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email }),
  })

  if (!response.ok) throw new Error('Submission failed')
}
Set in .env:
VITE_WAITLIST_API_ENDPOINT=https://your-project.supabase.co/rest/v1/waitlist
Option 2: Use ConvertKit, Mailchimp, etc. Replace the fetch call with your email service’s API endpoint and required headers.

πŸš€ Deployment

  1. Push your code to GitHub
  2. Go to vercel.com
  3. Import your repository
  4. Set Root Directory to apps/web
  5. Add environment variables from your .env file
  6. Deploy!
The landing page includes a vercel.json configuration file for easy deployment.

Deploy to Netlify

  1. Go to netlify.com
  2. Import your repository
  3. Set Base directory to apps/web
  4. Set Build command to yarn build
  5. Set Publish directory to apps/web/dist
  6. Add environment variables
  7. Deploy!

Other Platforms

The marketing page is a standard Vite app, so it works with:
  • Cloudflare Pages
  • GitHub Pages
  • AWS Amplify
  • Any static host
Build command: yarn build (from apps/web/) Output directory: dist/

πŸ“± SEO Configuration

The landing page includes SEO components that set:
  • Meta title and description
  • Open Graph tags (for social sharing)
  • Twitter cards
  • Canonical URL
Configure these in your .env:
VITE_APP_NAME=YourApp  # Used as page title
VITE_APP_DESCRIPTION=Your description  # Used in meta description
VITE_OG_IMAGE=https://yourapp.com/og-image.jpg  # Social share image
VITE_APP_URL=https://yourapp.com  # Canonical URL
Pro Tip: Create an Open Graph image (1200x630px) for better social sharing. Place it in /apps/web/public/og-image.jpg.

🎯 vs. Main Landing Page Repo

You might be wondering: β€œWhat’s the difference between apps/web and the landing_page repo?”
Featureapps/weblanding_page
PurposeYour app’s marketing pageShipnative’s own marketing
IncludedYes, in the monorepoSeparate repository
CustomizationYou own it - edit freelyDon’t edit (it’s for Shipnative)
Use casePre-launch waitlist or launch pageShipnative product marketing
DeployYour domainshipnative.com
TL;DR: Use apps/web for YOUR app. Ignore landing_page (unless you’re contributing to Shipnative itself).

πŸ’‘ Tips & Best Practices

Pre-Launch Strategy

  1. Start in waitlist mode while building
  2. Share the link on Twitter, Product Hunt, Reddit
  3. Collect 100-1000+ emails before launch
  4. Send launch announcement when app goes live

Launch Day

  1. Switch to launch mode (VITE_MODE=launch)
  2. Add your App Store / Play Store URLs
  3. Redeploy
  4. Email your waitlist!

A/B Testing

Want to test different copy? Create multiple deployments:
  • waitlist-v1.yourapp.com
  • waitlist-v2.yourapp.com
Deploy the same code with different env vars and see which converts better.

Custom Domain

  1. Buy a domain (Namecheap, Google Domains, etc.)
  2. Point it to your Vercel/Netlify deployment
  3. Configure in your hosting dashboard
  4. Update VITE_APP_URL to match

πŸ†˜ Troubleshooting

  • Make sure your .env file is in apps/web/, not the monorepo root
  • Restart the dev server after changing .env
  • Vite requires VITE_ prefix for all public env vars
  • Check for typos in variable names
  • Verify the image exists in /apps/web/public/
  • Check that VITE_APP_SCREENSHOT_URL matches your filename
  • Try using an absolute URL instead of a relative path
  • Clear browser cache
  • Check browser console for errors
  • Verify VITE_WAITLIST_API_ENDPOINT is correctly set
  • Test your API endpoint with Postman/Insomnia first
  • Check CORS settings on your API
  • Run yarn install in apps/web/
  • Verify tailwind.config.cjs exists
  • Check that src/index.css is imported in src/main.tsx
  • Try yarn build to see if it’s a dev-only issue

πŸŽ“ Next Steps