logo

Nurav UI

Community
Pricing Section

Classic Pricing

Clean, minimalist 3-card layout with subtle hover effects and staggered animations.Copy Markdown

Preview

A sleek, three-tiered pricing section. The middle card emphasizes the most popular plan with shadow and border highlights.

Loading...

Usage with Props

You can customize the classic pricing section by passing your own tiers and content.

import { ClassicPricing, PricingPlan } from "@/components/nurav-ui/ClassicPricing";

export default function App() {
  const customPlans: PricingPlan[] = [
    {
      name: "Standard",
      price: "$15",
      description: "For small teams.",
      features: ["Up to 5 Projects", "Basic Support"],
    },
    {
      name: "Ultra",
      price: "$45",
      description: "For growing companies.",
      features: ["Unlimited Projects", "Priority Support", "Advanced Analytics"],
      isPopular: true,
    },
    {
      name: "Max",
      price: "$95",
      description: "For high-scale enterprises.",
      features: ["Everything in Ultra", "24/7 Phone Support", "SSO Integration"],
    }
  ];

  return (
    <div className="w-full">
      <ClassicPricing 
        title="Predictable Pricing" 
        description="Choose a plan that works best for your team's needs." 
        plans={customPlans} 
      />
    </div>
  );
}

Installation

Automatic (CLI)

The fastest way to install this pricing block is using the setup CLI.

Before using the @nurav-ui alias, add this to your components.json:

components.json
{
  "registries": {
    "@nurav-ui": "https://nurav-ui.vercel.app/r"
  }
}

Or skip alias setup entirely and use the direct URL below.

Terminal
npx shadcn@latest add @nurav-ui/classic-pricing

Alternatively, install via direct URL (no components.json changes needed):

Terminal
npx shadcn@latest add https://nurav-ui.vercel.app/r/classic-pricing.json

Manual Setup

If you prefer building it manually, you can just copy and paste the code below directly into your project.

Create the Component

Add the following code to components/nurav-ui/ClassicPricing.tsx:

ClassicPricing.tsx
"use client";

import React from "react";
import { motion, Variants } from "motion/react";
import { Check } from "lucide-react";
import { cn } from "@/lib/utils";

export interface PricingPlan {
  name: string;
  price: string;
  description: string;
  features: string[];
  isPopular?: boolean;
}

export interface ClassicPricingProps {
  title?: React.ReactNode;
  description?: React.ReactNode;
  plans?: PricingPlan[];
}

const defaultPlans: PricingPlan[] = [
  {
    name: "Starter",
    price: "$19",
    description: "Perfect for individuals and small side projects.",
    features: [
      "Up to 5 projects",
      "Basic analytics",
      "24-hour support response time",
      "Custom domains",
    ],
  },
  {
    name: "Pro",
    price: "$49",
    description: "Ideal for growing businesses and professional teams.",
    features: [
      "Unlimited projects",
      "Advanced analytics",
      "1-hour support response time",
      "Custom domains",
      "Team collaboration",
    ],
    isPopular: true,
  },
  {
    name: "Enterprise",
    price: "$99",
    description: "For large scale organizations with advanced needs.",
    features: [
      "Unlimited everything",
      "Custom reporting",
      "24/7 dedicated phone support",
      "Custom domains",
      "Advanced security",
      "SSO integration",
    ],
  },
];

export const ClassicPricing = ({
  title = "Simple, transparent pricing",
  description = "Choose the perfect plan for your needs. No hidden fees.",
  plans = defaultPlans,
}: ClassicPricingProps) => {
  const containerVariants = {
    hidden: { opacity: 0 },
    visible: {
      opacity: 1,
      transition: {
        staggerChildren: 0.1,
      },
    },
  };

  const itemVariants: Variants = {
    hidden: { opacity: 0, y: 20 },
    visible: {
      opacity: 1,
      y: 0,
      transition: { duration: 0.6, ease: [0.16, 1, 0.3, 1] },
    },
  };

  return (
    <section className="w-full py-8 px-3 bg-background">
      <div className="max-w-7xl mx-auto">
        <div className="text-center max-w-3xl mx-auto mb-16">
          <motion.h2
            initial={{ opacity: 0, y: -20 }}
            animate={{ opacity: 1, y: 0 }}
            className="text-4xl font-bold tracking-tight text-foreground mb-4"
          >
            {title}
          </motion.h2>
          <motion.p
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            transition={{ delay: 0.1 }}
            className="text-lg text-muted-foreground"
          >
            {description}
          </motion.p>
        </div>

        <motion.div
          variants={containerVariants}
          initial="hidden"
          animate="visible"
          className="grid md:grid-cols-3 gap-8"
        >
          {plans.map((plan, idx) => (
            <motion.div
              key={idx}
              variants={itemVariants}
              whileHover={{ y: -8 }}
              className={cn(
                "relative flex flex-col p-8 rounded-3xl border transition-all duration-300",
                plan.isPopular
                  ? "border-primary shadow-xl shadow-primary/10 bg-primary/2"
                  : "border-foreground/10 bg-card hover:border-foreground/20",
              )}
            >
              {plan.isPopular && (
                <div className="absolute -top-4">
                  <span className="bg-primary text-primary-foreground text-xs font-bold uppercase px-2 py-1 rounded-full">
                    Most Popular
                  </span>
                </div>
              )}

              <div className="mb-5">
                <h3 className="text-xl font-semibold text-foreground mb-2">
                  {plan.name}
                </h3>
                <p className="text-muted-foreground text-sm leading-relaxed min-h-[40px]">
                  {plan.description}
                </p>
              </div>

              <div className="mb-8 font-serif">
                <span className="text-5xl font-black text-foreground">
                  {plan.price}
                </span>
                <span className="text-muted-foreground font-sans">/mo</span>
              </div>

              <ul className="flex-1 space-y-4 mb-8">
                {plan.features.map((feature, i) => (
                  <li key={i} className="flex items-start gap-3">
                    <div className="mt-1 bg-primary/10 rounded-full p-1">
                      <Check
                        className="w-3.5 h-3.5 text-primary"
                        strokeWidth={3}
                      />
                    </div>
                    <span className="text-foreground/80 text-sm leading-relaxed">
                      {feature}
                    </span>
                  </li>
                ))}
              </ul>

              <button
                className={cn(
                  "w-full py-4 rounded-xl font-semibold transition-all duration-300",
                  plan.isPopular
                    ? "bg-primary text-primary-foreground hover:bg-primary/90"
                    : "bg-foreground/5 text-foreground hover:bg-foreground/10",
                )}
              >
                Get Started
              </button>
            </motion.div>
          ))}
        </motion.div>
      </div>
    </section>
  );
};

Props

ClassicPricingProps

PropTypeDefaultDescription
title
ReactNode"Simple, transparent pricing"The main heading of the pricing section.
description
ReactNode"Choose the perfect plan..."The subtitle text below the heading.
plans
PricingPlan[]defaultPlansArray of pricing plans to display.

PricingPlan

PropTypeDefaultDescription
namerequired
string—The title of the plan (e.g. "Starter").
pricerequired
string—The price string (e.g. "$19").
descriptionrequired
string—A short description for the tier.
featuresrequired
string[]—Array of feature strings included.
isPopular
boolean—If true, highlights the card as the most popular.

Built by Varun

Last Updated:April 7, 2026

On this page