Build a Pricing Page with HeroUI Cards and a Toggle in Next.js

A great pricing page inspires confidence. It shows clarity, flexibility, and trust. When you design it well, users decide faster. In this tutorial, you will build a pricing page with HeroUI in Next.js. You will use a toggle switch to switch between monthly and yearly pricing. Cards will showcase the plans. TypeScript ensures type safety and a better developer experience.

Why a Pricing Page Matters

Pricing pages are not just about numbers. They drive conversions. A clear design persuades users to choose a plan. A confusing layout, however, drives them away. Transparency matters, and users must be able to understand options instantly. That’s why you will use HeroUI. It provides accessible, modern, and responsive components. Combined with Next.js, it delivers blazing-fast performance. Together, they create a pricing page that inspires trust.

Setting Up the Project

Project Structure

pricing-page/
├── app/
│   ├── pricing/
│   │   └── page.tsx         # Pricing page with cards and toggle
│   ├── layout.tsx           # Root layout for Next.js
│   └── globals.css          # Tailwind global styles
├── components/
│   ├── PricingToggle.tsx    # Switch component for monthly/yearly toggle
│   └── PricingCard.tsx      # HeroUI Card component for plan details
├── public/                  # Static assets like images or icons
├── styles/
│   └── theme.css            # Optional extra theme or overrides
├── tailwind.config.js       # Tailwind configuration
├── tsconfig.json            # TypeScript configuration
├── package.json             # Project dependencies and scripts
└── README.md                # Documentation for your project

Key Points

  • app/pricing/page.tsx: The main page, powered by Next.js App Router.
  • components/: Holds reusable UI blocks like cards and toggles.
  • public/: Stores logos, icons, and static media.
  • styles/: Extra styling or global overrides.
  • tailwind.config.js: Central place for customizing design tokens.

This structure keeps logic, UI, and configuration separated. It prevents bloated files and ensures maintainability. As your app grows, you can add more folders like hooks/ or utils/.

Creating the Pricing Toggle

The toggle is the heart of your page. It allows users to choose between monthly and yearly billing. Add a component PricingToggle.tsx:

"use client";

import { Switch } from "@heroui/react";
import { useState } from "react";

export default function PricingToggle({
  onChange,
}: {
  onChange: (isYearly: boolean) => void;
}) {
  const [isYearly, setIsYearly] = useState(false);

  return (
    <div className="flex items-center justify-center gap-2 my-6">
      <span>Monthly</span>
      <Switch
        isSelected={isYearly}
        onValueChange={(checked) => {
          setIsYearly(checked);
          onChange(checked);
        }}
      />
      <span>Yearly</span>
    </div>
  );
}

This toggle communicates billing preferences to the parent component. It starts with monthly as the default.

Designing the Pricing Card

A pricing card shows the plan. It includes title, price, features, and a button. HeroUI’s Card component makes this simple. Create PricingCard.tsx:

import { Card, CardBody, CardHeader, Button } from "@heroui/react";

type PricingCardProps = {
  title: string;
  price: string;
  features: string[];
};

export default function PricingCard({
  title,
  price,
  features,
}: PricingCardProps) {
  return (
    <Card className="max-w-lg">
      <CardHeader className="text-center text-xl font-bold">{title}</CardHeader>
      <CardBody>
        <div className="text-center text-3xl font-semibold">{price}</div>
        <ul className="mt-4 space-y-2">
          {features.map((feature, idx) => (
            <li key={idx} className="text-gray-600">
              {feature}
            </li>
          ))}
        </ul>
        <div className="mt-6 flex justify-center">
          <Button color="primary">Choose Plan</Button>
        </div>
      </CardBody>
    </Card>
  );
}

The card displays a clean layout. It encourages interaction and simplifies plan comparison.

Combining Cards and Toggle

Now, assemble the pricing page. Create app/pricing/page.tsx:

"use client";

import { useState } from "react";

import PricingToggle from "../../components/PricingToggle";
import PricingCard from "../../components/PricingCard";

export default function PricingPage() {
  const [isYearly, setIsYearly] = useState(false);

  const plans = [
    {
      title: "Starter",
      monthly: "$10",
      yearly: "$100",
      features: ["1 Project", "Basic Support", "Community Access"],
    },
    {
      title: "Pro",
      monthly: "$30",
      yearly: "$300",
      features: ["10 Projects", "Priority Support", "Team Access"],
    },
    {
      title: "Enterprise",
      monthly: "$100",
      yearly: "$1000",
      features: ["Unlimited Projects", "Dedicated Support", "Custom Solutions"],
    },
  ];

  return (
    <main className="flex flex-col items-center p-6">
      <h1 className="text-4xl font-bold text-center mb-6">Choose Your Plan</h1>
      <PricingToggle onChange={setIsYearly} />
      <div className="grid gap-6 md:grid-cols-3">
        {plans.map((plan, idx) => (
          <PricingCard
            key={idx}
            features={plan.features}
            price={isYearly ? plan.yearly : plan.monthly}
            title={plan.title}
          />
        ))}
      </div>
    </main>
  );
}

This page connects the toggle to the cards. When the toggle changes, the price updates instantly. The experience feels smooth and responsive.

Launch an application

>npm run dev
http://localhost:3000/pricing
Monthly
Yearly

Improve Design with Tailwind CSS

PricingCard.tsx

import { Card, CardBody, CardHeader, Button } from "@heroui/react";
import { CheckBadgeIcon } from "@heroicons/react/24/solid";
type PricingCardProps = {
  title: string;
  price: string;
  features: string[];
};

export default function PricingCard({
  title,
  price,
  features,
}: PricingCardProps) {
  return (
    <Card className="max-w-sm rounded-2xl shadow-xl overflow-hidden ease-in-out bg-white dark:bg-gray-800">
      <CardHeader className="text-center text-2xl font-extrabold p-6 bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600">
        {title}
      </CardHeader>
      <CardBody className="p-8">
        <div className="text-center text-4xl font-extrabold text-blue-600 dark:text-blue-400">
          {price}
        </div>
        <ul className="mt-6 space-y-4">
          {features.map((feature, idx) => (
            <li
              key={idx}
              className="text-gray-700 dark:text-gray-300 flex items-center"
            >
              <CheckBadgeIcon className="text-green-500 h-6 w-6" />
              {feature}
            </li>
          ))}
        </ul>
        <div className="mt-8 flex justify-center">
          <Button
            className="w-full py-3 px-6 rounded-full font-bold text-lg hover:shadow-lg transition-shadow duration-300"
            color="primary"
          >
            Choose Plan
          </Button>
        </div>
      </CardBody>
    </Card>
  );
}
Pricing page

Add an icon in the switch component

PricingToggle.tsx:

"use client";

import { Switch } from "@heroui/react";
import { useState } from "react";
import { CalendarIcon, GiftIcon } from "@heroicons/react/24/solid";

export default function PricingToggle({
  onChange,
}: {
  onChange: (isYearly: boolean) => void;
}) {
  const [isYearly, setIsYearly] = useState(false);

  return (
    <div className="flex items-center justify-center gap-2 my-6">
      <span>Monthly</span>
      <Switch
        endContent={<CalendarIcon className="h6 w6" />}
        isSelected={isYearly}
        size="lg"
        startContent={<GiftIcon className="h6 w6" />}
        onValueChange={(checked) => {
          setIsYearly(checked);
          onChange(checked);
        }}
      />
      <span>Yearly</span>
    </div>
  );
}
Icon

Making It Inspirational

Your pricing page must inspire trust. Highlight the value, not just the cost. Use persuasive words in headings. Example: “Invest in Growth” instead of “Pricing Plans.” This mindset encourages users to see value beyond numbers. A toggle adds control. Users feel empowered to choose billing that suits them.

Best Practices for Pricing Pages

How to optimize pricing pages?

Why Next.js and HeroUI Shine Together

Next.js ensures fast loading with server-side rendering. HeroUI ensures an accessible and polished UI. Together, they speed up development and enhance UX. With TypeScript, you reduce errors. This stack makes you productive and gives users confidence.

Expanding Further

You can expand this pricing page. Add animations for the toggle. Highlight the most popular plan. Include testimonials below the cards. Show currency localization if you target global markets. Every extra touch improves trust and boosts conversions.

Finally

A pricing page is more than a table of numbers. It is persuasion through clarity, design, and trust. Using Next.jsHeroUI, and TypeScript, you can build a professional, conversion-driven page. A toggle adds flexibility, and HeroUI cards make plans attractive. You now hold the steps to inspire users to act. Build with intention, and your pricing page will become your best salesperson.

This article was originally published on Medium.

Leave a Comment

Your email address will not be published. Required fields are marked *