Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelog
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone Select
Forms
Address InputCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowPost Listing WizardPricing PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAI Product PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePress PageProduct Tour PagePromo PageReferral PageResearch Report PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageSwitch PageTemplate Gallery PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Templates

Promo Page

Templates

Promo Page React template for Tailwind CSS v4

Design
Product Tour PageReferral Page
Design
Live props
Appearance
design
alternate
background
backgroundColor
bandBackground
bandColor
tone
ctaTone
offerColor
State
phase
defaultSeats
defaultPlan
Content
discount
code
startsAt
endsAt
Behavior
tick
timeZone
maxSeats
animateIn
locale
currency
Layout
showAnnouncement
showCountdown
showCode
showSpots
showPlans
showCalculator
showExtras
showTestimonials
showTerms
showFaq
showCta
showStickyBar
header
footer
Code
import { PromoPage } from "@/components/templates/promo-page"

<PromoPage />
  1. Home
  2. Templates
  3. Promo Page
Pro

Promo Page React template for Tailwind CSS v4

v1.0.0·Added in 1.9.0

A limited-time sale page: a drawn coupon hero with a countdown and copyable code, plan cards with live totals and a savings calculator.

The hero rises in once on mount beside a drawn composition: a perforated coupon for 40% off with notches, a barcode stub and the code, in front of two gift tags with the Pro and Business deal prices and a starburst sticker. The countdown ticks from a fixed clock (so server and client agree) to the end of the sale, or to its start in the teaser phase. Claim the deal jumps to the plans; the dashed code chip copies ACME40 with a check and a screen reader announcement. A progress bar shows how many onboarding spots are claimed. Each plan card shows the deal price beside the crossed-out regular one; its seats stepper (buttons, arrows, typing) rolls the first-year total and the savings live. Claim spins for 900 ms (or while onClaim runs), then shows the code applied with the seats and total and a Continue to checkout link, or an error if onClaim rejects; Change seats goes back. In the savings calculator the team size slider and the Pro / Business switch roll the amount saved and the two price bars; Use these seats copies them into that plan's card and scrolls to it. Bonus tiles, quotes from last year's sale, plain-English terms (one open at a time) and the FAQ follow, then a closing band with its own countdown. Teaser swaps the offer for a validated notify-me form (an error under the field, then success) with the number blurred on the coupon; last call turns the pill, label and countdown red; ended stamps the coupon, shows regular prices and a waitlist form. On phones a bar pinned to the bottom (above the safe area) shows the lowest price and the time left once the hero scrolls away, with a Claim (or Notify me) button. Reduced motion keeps everything still.

Built with

The sections this page is made of. In the full-screen preview, hold Alt to outline each one on the page.

  • Marketing Sectionmarketing-section
  • Marketing Headermarketing-header
  • Marketing Footermarketing-footer
  • Testimonials Sectiontestimonials-section
  • FAQ Sectionfaq-section

Add to a project

npx wingo-ui@latest add promo-page

Pro item: the CLI needs a Pro account. See plans

Usage

Import PromoPage from @/components/templates/promo-page.

// app/(marketing)/deals/page.tsx
"use client"

import Link from "next/link"
import { getPromoPhase, PromoPage } from "@/components/templates/promo-page"
import { PROMO_ENDS_AT, PROMO_STARTS_AT } from "@/components/templates/promo-page-data"

export default function DealsRoute() {
  return (
    <PromoPage
      linkComponent={Link}
      phase={getPromoPhase(PROMO_STARTS_AT, PROMO_ENDS_AT)}
      now={null}
      ctaTone="brand"
      onClaim={async (plan, seats) => {
        const response = await fetch("/api/checkout", { method: "POST", body: JSON.stringify({ plan: plan.id, seats, code: "ACME40" }) })
        if (!response.ok) throw new Error("Could not apply the deal")
      }}
      onNotify={async (email) => {
        await fetch("/api/sale-alerts", { method: "POST", body: JSON.stringify({ email }) })
      }}
    />
  )
}

Props

  • designdefault "classic"
    "classic" | "bold" | "minimal"

    The whole page's look. classic: a split hero with the coupon and gift tags over a sunburst, a dark closing band; bold: the all-black Black Friday page in both themes, a pill header, a giant title in the offer color under a night-blue glow, offer-colored countdown tiles, rounder cards, a quote wall, FAQ cards, an offer-colored closing band and a wordmark footer; minimal: a transparent header, a centered hero with the coupon alone, outlined flat cards, no bands, a ruled closing band and a simple footer. The *Props overrides still win.

  • phasedefault "live"
    "teaser" | "live" | "last-call" | "ended"

    Where the sale is. teaser: a countdown to the start, a notify-me form and locked plan cards; live: the offer with a countdown to the end; last-call: the same with red urgency in the pill, label, countdown and phone bar; ended: a stamped coupon, regular prices, a waitlist form and no calculator, bonuses or terms. getPromoPhase computes it from the dates.

  • discountdefault 40
    number

    Percent off. It flows into every label ({discount}), the coupon, the stickers and every price and total.

  • codedefault "ACME40"
    string

    The promo code on the coupon, the copy chips, the checkout link and the labels ({code}).

  • startsAtdefault "2026-11-27T08:00:00Z"
    Date | number | string

    When the sale starts (Nov 27 at 12:00 AM PT): the teaser countdown and {start} in the copy.

  • endsAtdefault "2026-12-02T07:59:59Z"
    Date | number | string

    When the sale ends (Dec 1 at 11:59 PM PT): the countdowns and {end} in the copy.

  • nowdefault "PROMO_SAMPLE_NOW[phase]"
    Date | number | string | null

    The clock the countdowns start from. The default is a fixed sample per phase (a week before, mid-sale, the final hours, the day after) so server and client render the same digits; null uses the visitor's real clock.

  • tickdefault true
    boolean

    The countdowns run on from now after hydration; false freezes them (screenshots).

  • timeZonedefault "America/Los_Angeles"
    string

    The IANA zone the dates in the copy are formatted in, so every visitor reads the same day.

  • plansdefault "PROMO_PLANS"
    PromoPlan[]

    The offer cards: { id: "pro" | "business", name, description, yearlyMonthly (regular price per user per month, billed yearly), monthly, featuresTitle?, features, seats (on load), badge?, featured? }.

  • extrasdefault "PROMO_EXTRAS"
    PromoExtra[]

    The bonus tiles: { id, icon?, tag, title, description, value }.

  • testimonialsdefault "PROMO_TESTIMONIALS"
    Testimonial[]

    Quotes from last year's sale: { quote, name, role?, company?, companyIcon?, metric? }.

  • termsdefault "PROMO_TERMS"
    PromoTerm[]

    The plain-English terms: { id, title, content }; strings may hold {discount}, {code}, {start} and {end}.

  • faqdefault "PROMO_FAQ"
    FaqItem[]

    { question, answer } pairs; string answers may hold the same placeholders.

  • spotsdefault "{ claimed: 2418, total: 5000 }"
    PromoSpots

    The onboarding spots progress in the hero.

  • labelsdefault "DEFAULT_PROMO_PAGE_LABELS"
    Partial<PromoPageLabels>

    Every visible string, with {discount}, {code}, {start}, {end} and {year} filled in (a few take more, like {plan} or {seats}).

  • hrefsdefault "DEFAULT_PROMO_PAGE_HREFS"
    Partial<PromoPageHrefs>

    { checkout, pricing, sales, terms }. The checkout link gets ?plan=&seats=&code= appended.

  • defaultSeatsdefault 25
    number

    The savings calculator's team size on load.

  • defaultPlandefault "business"
    "pro" | "business"

    The savings calculator's plan on load.

  • maxSeatsdefault 200
    number

    The most seats the steppers and the calculator's slider go to.

  • showAnnouncementdefault true
    boolean

    The strip above the header bar.

  • showCountdowndefault true
    boolean

    The countdowns in the hero and the closing band.

  • showCodedefault true
    boolean

    The copyable code chips.

  • showSpotsdefault true
    boolean

    The onboarding spots progress in the hero (live and last call).

  • showPlansdefault true
    boolean

    The plan cards.

  • showCalculatordefault true
    boolean

    The savings calculator (hidden once the sale ended).

  • showExtrasdefault true
    boolean

    The bonus tiles (hidden once the sale ended).

  • showTestimonialsdefault true
    boolean

    The quotes from last year's sale.

  • showTermsdefault true
    boolean

    The plain-English terms (hidden once the sale ended).

  • showFaqdefault true
    boolean

    The FAQ.

  • showCtadefault true
    boolean

    The closing band.

  • showStickyBardefault true
    boolean

    The phone bar pinned to the bottom once the hero scrolls away (below 768px, not when ended).

  • alternatedefault true
    boolean

    Bands in the bandBackground look on every other visible section. Applies to the classic and bold designs.

  • backgrounddefault "aurora"
    "plain" | "soft" | "glow" | "aurora" | "grid" | "dots"

    The main background: plain is the bare page color; the others tint the page and add a glow, an aurora, a grid or dots over the first screen. A design picks its own unless you set one. classic uses aurora, bold a glow with gradient bands, minimal plain; on a plain page the bold hero draws its own offer-colored spotlight.

  • backgroundColordefault "oklch(0.62 0.14 275)"
    string

    The tint and glow color of the main background.

  • bandBackgrounddefault "muted"
    "muted" | "tinted" | "gradient" | "dots"

    The secondary background of the alternating bands. Cards, fields and tiles inside a band are lifted one step above it, so they never share its color.

  • bandColordefault "oklch(0.62 0.14 275)"
    string

    The tint of tinted and gradient bands; defaults to backgroundColor.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    Checks, the slider, the spots bar, badges and the offer color when offerColor is empty.

  • ctaTonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    Claim buttons, the notify form, the phone bar and the header's Get started.

  • offerColordefault "#FFD43B"
    string

    The festive color of the coupon, the savings stickers, the deal bar and the bold design's title and closing band. Text on it turns black or white by lightness. Empty follows tone.

  • headerdefault true
    boolean

    The marketing header; off inside a site layout.

  • footerdefault true
    boolean

    The marketing footer with its newsletter form.

  • headerProps
    Partial<MarketingHeaderProps>

    Passed to MarketingHeader.

  • footerProps
    Partial<MarketingFooterProps>

    Passed to MarketingFooter.

  • testimonialsProps
    Partial<TestimonialsSectionProps>

    Overrides for the TestimonialsSection.

  • faqProps
    Partial<FaqSectionProps>

    Overrides for the FaqSection.

  • onClaim
    (plan: PromoPlan, seats: number) => (void | Promise<unknown>)

    A plan's Claim button. A promise keeps it spinning, a rejection shows an error. Without it: 900 ms, then the code applied and a checkout link.

  • onNotify
    (email: string) => (void | Promise<unknown>)

    The notify-me (teaser) and waitlist (ended) forms; a rejection shows an error. Without it: 900 ms, then success.

  • onCopyCode
    (code: string) => void

    A code chip copied the code.

  • onSubscribe
    (email: string) => (void | Promise<unknown>)

    The footer newsletter; a rejected promise shows an error.

  • animateIndefault true
    boolean

    The hero rises in once and the coupon settles into place; false keeps the page still (thumbnails). Reduced motion always does.

  • localedefault "en-US"
    string

    Intl locale for prices, totals, counters and dates. Empty follows UIProvider.

  • currencydefault "USD"
    string

    ISO currency for every price; the plan amounts are taken as is.

  • linkComponentdefault "<a>"
    (props: LinkComponentProps) => ReactNode

    The router link for every link on the page (next/link in an app).

  • className
    string

    Classes for the root.

  • classNames
    Partial<Record<PromoPageSlot, string>>

    Classes for header, hero, pill, title, art, code, status, countdown, spots, notify, plans, plan, calculator, extras, extra, testimonials, terms, faq, cta, sticky and footer.

Parts

  • getPromoPhase

    function

    The phase a sale is in at a moment: (startsAt, endsAt, now = Date.now(), lastCallHours = 24) => "teaser" | "live" | "last-call" | "ended". Pair it with now={null} for a page that follows the real clock.

  • PROMO_PAGE_DESIGNS

    constant

    The design names: classic, bold and minimal.

  • DEFAULT_PROMO_PAGE_LABELS

    constant

    The English strings with their placeholders, to spread under a translation.

  • DEFAULT_PROMO_PAGE_HREFS

    constant

    /checkout, /pricing, /contact and /legal/terms.

  • PROMO_PAGE_SAMPLE

    constant

    All the sample content in one object: discount, code, dates, time zone, sample clocks, spots, plans, extras, quotes, terms and FAQ.

  • PROMO_PLANS

    constant

    Pro ($10 per user per month billed yearly, 10 seats) and Business ($20, 25 seats, Best value).

  • PROMO_EXTRAS

    constant

    Acme AI at half price for three months, a free onboarding session and a white-glove migration.

  • PROMO_TESTIMONIALS

    constant

    Three quotes from last year's sale with a metric each.

  • PROMO_TERMS

    constant

    Who can claim, how long the price lasts, seats, refunds, stacking and the deadline.

  • PROMO_FAQ

    constant

    Five questions about the code, billing, current plans, seat limits and next year.

  • PROMO_SAMPLE_NOW

    constant

    The fixed sample clock per phase: Nov 20, Nov 29, the afternoon of Dec 1 and Dec 3, 2026.

  • PROMO_SPOTS

    constant

    { claimed: 2418, total: 5000 }.

  • PROMO_CODE

    constant

    "ACME40".

  • PROMO_DISCOUNT

    constant

    40.

  • PROMO_STARTS_AT

    constant

    "2026-11-27T08:00:00Z", Nov 27 at 12:00 AM PT.

  • PROMO_ENDS_AT

    constant

    "2026-12-02T07:59:59Z", Dec 1 at 11:59 PM PT.

  • PROMO_TIME_ZONE

    constant

    "America/Los_Angeles".

  • PromoPageProps

    type

    Props of PromoPage.

  • PromoPageDesign

    type

    "classic" | "bold" | "minimal".

  • PromoPagePhase

    type

    "teaser" | "live" | "last-call" | "ended".

  • PromoPlan

    type

    One offer card.

  • PromoPlanId

    type

    "pro" | "business".

  • PromoExtra

    type

    One bonus tile.

  • PromoTerm

    type

    One plain-English term.

  • PromoSpots

    type

    { claimed, total }.

  • PromoPageLabels

    type

    The strings labels can override.

  • PromoPageHrefs

    type

    The link targets hrefs can override.

  • PromoPageSlot

    type

    The classNames keys.

Examples

Open

Bold design

design="bold": the all-black Black Friday page in both themes, a giant title in the offer color, offer-colored countdown tiles and closing band.

Open

Minimal design

design="minimal": a transparent header, a centered hero with the coupon alone, outlined cards, no gray bands and a simple footer.

Open

Teaser before the sale

phase="teaser": a countdown to Nov 27, a blurred number on the coupon, a validated notify-me form and locked plan cards.

Open

Last call in brand orange

phase="last-call" with ctaTone brand: red urgency on the pill, label and countdown, final hours left.

Open

Sale ended

phase="ended": a stamped coupon, regular prices with links to pricing and a waitlist for next year.

Open

Cyber Monday at 30% off, checkout down

discount 30 with code CYBER30 flows into every string and price; an onClaim that rejects shows the error in the card.

Dependencies

lucide-reactmotionmarketing-sectionmarketing-headermarketing-footertestimonials-sectionfaq-sectionaccordionanimated-counterbadgebuttoncopy-buttoncountdownnumber-inputprogresssegmented-controlslidermotionnavigationtonesui-config

Files

  • components/templates/promo-page.tsx
  • components/templates/promo-page-data.ts

See also

Pricing PageLaunch PageCountdownCopy ButtonLanding Page
PreviousProduct Tour PageNext Referral Page