Skip to main content

Plans Config

Your pricing tiers — free, premium, and beyond.

File: config/plans.config.ts

This file defines the plans shown on the /pricing page and in the submission flow. Each paid plan must have a matching Stripe Price ID — you create prices in Stripe, then paste the ID into an env var.

plans.config.ts open in IDE

The default setup

Two plans: Standard (free, $0) and Premium ($15 one-time).

config/plans.config.ts
export const plansConfig: PlansConfig = {
  currency: 'USD',
 
  plans: [
    {
      id: 'standard',
      name: 'Standard',
      price: 0,
      type: 'free',
      description: 'Free listing with basic features',
      priceId: null,
      cta: 'Get Started Free',
      features: {
        homepage_duration: 30,
        guaranteed_backlinks: 0,
        premium_badge: false,
        skip_queue: false,
        social_promotion: false,
        priority_support: false,
      },
    },
    {
      id: 'premium',
      name: 'Premium',
      price: 15,
      type: 'one-time',
      description: 'Premium listing with priority placement',
      priceId: process.env.STRIPE_PRICE_ID_PREMIUM || null,
      cta: 'Go Premium',
      highlighted: true,
      features: {
        homepage_duration: 7,
        guaranteed_backlinks: 3,
        premium_badge: true,
        skip_queue: true,
        social_promotion: true,
        priority_support: true,
        detailed_analytics: true,
        founder_outreach: true,
      },
    },
  ],
};

Fields

FieldTypeWhat it does
idstringInternal ID. Used by API + getPlan('id').
namestringDisplay name on the pricing card.
pricenumberPrice in your currency (dollars, not cents).
type'free' | 'one-time' | 'recurring'Billing mode.
descriptionstringOne-line description under the plan name.
priceIdstring | nullThe Stripe Price ID — from STRIPE_PRICE_ID_* env.
ctastringButton label.
highlightedboolean?Adds the "most popular" highlight.
featuresobjectArbitrary keys describing what the plan includes. Used for the feature-comparison table.

Pricing page preview

The /pricing page with configured plans

Adding a new plan

1. Create a Price in Stripe

  1. Stripe Dashboard → ProductsAdd product
  2. Name it ("Pro", "Lifetime", whatever)
  3. Set the price and type (one-time or recurring)
  4. Save → copy the Price ID (starts with price_)

See Payments for the full Stripe workflow.

2. Add the env var

Append to .env.local:

STRIPE_PRICE_ID_PRO=price_1ABCdef...

3. Add the plan object

plans: [
  // ...existing plans,
  {
    id: 'pro',
    name: 'Pro',
    price: 29,
    type: 'one-time',
    description: 'Everything in Premium plus...',
    priceId: process.env.STRIPE_PRICE_ID_PRO || null,
    cta: 'Get Pro',
    features: {
      homepage_duration: 14,
      guaranteed_backlinks: 10,
      premium_badge: true,
      skip_queue: true,
      social_promotion: true,
      priority_support: true,
      dedicated_account_manager: true,
    },
  },
],

4. Restart the dev server

Env var changes don't hot-reload. Stop (Ctrl+C) and run pnpm dev again.

Or, design the pricing with AI

AI Prompt· Design pricing plans

Update config/plans.config.ts for my directory about {your niche}.

Business model:

  • Free tier: {what free users get / limits}
  • Paid tier(s): {e.g. "$15 one-time Premium with priority placement and guaranteed backlinks"}
  • Currency: {USD|EUR|RUB|...}

For each plan, pick sensible values in the features object (homepage_duration, guaranteed_backlinks, premium_badge, skip_queue, social_promotion, priority_support, etc.) that match the pitch.

For paid plans, reference process.env.STRIPE_PRICE_ID_\{PLAN_ID\} on priceId — don't hardcode IDs. List the env vars I need to add afterwards.

Preserve the PlansConfig type. Don't touch helper functions.

Helper functions

The config also exports:

import { getPlan, getFreePlan, getPaidPlans } from '@/config/plans.config';
 
getPlan('premium');   // → the Premium plan object, or undefined
getFreePlan();        // → first plan with price 0
getPaidPlans();       // → all plans with price > 0

Going free-only

If you don't want to collect money at all:

  1. Remove the premium plan (keep only standard)
  2. In config/payments.config.ts, set provider: 'none'

The /pricing page will show only the free plan, and the Stripe integration becomes inert.