Skip to main content

Advertising Config

Pricing for sponsors and paid placements.

File: config/advertising.config.ts

Controls pricing and limits for the three paid placements and for sponsor subscriptions. Each placement needs a matching Stripe Price ID.

What this controls

Sponsors

Monthly subscription for logos in the sidebar / partners section.

sponsors: {
  maxSponsors: 8,
  priceId: process.env.STRIPE_PRICE_ID_PARTNER_SUBSCRIPTION || null,
},
FieldMeaning
maxSponsorsHow many sponsor slots are active at any time. Extra buyers go on a waitlist.
priceIdRecurring Stripe price — created in Stripe Dashboard.

Promotions (three placement types)

promotions: {
  ctaMaxLength: 20,
  ctaDefault: 'Visit {name}',
  allThreeDiscountPercent: 0.3,
  allThreeDiscountCouponId: process.env.STRIPE_COUPON_ID_PROMO_ALL_THREE || null,
  minPricePerMonth: 19,
  placements: {
    banner: {
      id: 'banner',
      name: 'Top Banner Ad',
      description: 'Horizontal banner below the header',
      pricePerMonth: 69,
      priceId: process.env.STRIPE_PRICE_ID_PROMO_BANNER || null,
      maxActive: 1,
    },
    catalog: {
      id: 'catalog',
      name: 'Catalog Ad Card',
      description: 'Promoted card in the project grid',
      pricePerMonth: 39,
      priceId: process.env.STRIPE_PRICE_ID_PROMO_CATALOG || null,
      maxActive: 2,
    },
    detailPage: {
      id: 'detail_page',
      name: 'Project Page Ad Card',
      description: 'Sidebar card on project detail pages',
      pricePerMonth: 19,
      priceId: process.env.STRIPE_PRICE_ID_PROMO_DETAIL || null,
      maxActive: 2,
    },
  },
},

Fields explained

FieldMeaning
ctaMaxLengthMax characters in the custom button text on a promo card.
ctaDefaultFallback CTA if user didn't set one. {name} is replaced with the project name.
allThreeDiscountPercentDiscount when buyer picks all three placements at checkout.
allThreeDiscountCouponIdStripe coupon ID used to apply that discount.
minPricePerMonthCheapest placement price — used as "starts at" on the /promote page.
maxActiveHow many of that placement type can run simultaneously.

Stripe setup

For each placement, create a recurring (monthly) Price in Stripe and paste the ID into .env.local:

STRIPE_PRICE_ID_PROMO_BANNER=price_...
STRIPE_PRICE_ID_PROMO_CATALOG=price_...
STRIPE_PRICE_ID_PROMO_DETAIL=price_...
STRIPE_PRICE_ID_PARTNER_SUBSCRIPTION=price_...

For the "buy all three = 30% off" coupon:

  1. Stripe Dashboard → CouponsCreate
  2. Percentage off: 30, Duration: forever
  3. Copy the coupon ID → STRIPE_COUPON_ID_PROMO_ALL_THREE

Turning promotions off entirely

Disable the promotions flag in features.config.ts. The /promote page returns 404, the API routes return 404, the admin section disappears.

With AI

AI Prompt· Design promotion & sponsor pricing

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

My monetization plan:

  • Banner (top of every page) — {price}/month, {number} slot
  • Catalog ad card (mixed into project grid) — {price}/month, {number} slots
  • Project-page ad card (sidebar) — {price}/month, {number} slots
  • Bundle discount if buying all three: {percentage}% off
  • Sponsors (logos in sidebar / partners section) — {price}/month recurring, {max} active

Default CTA fallback when buyer doesn't set one: "Visit {name}" Max chars for custom CTA: 20

Set pricePerMonth, maxActive, and helper fields accordingly. Reference env vars (STRIPE_PRICE_ID_PROMO_*, STRIPE_PRICE_ID_PARTNER_SUBSCRIPTION, STRIPE_COUPON_ID_PROMO_ALL_THREE) on the priceId / couponId fields — don't hardcode. List the env vars I need to add.

See also: Promotions & Sponsors