Skip to main content
Use Case

Build a Pricing Page with React and Tailwind CSS v4

Build a responsive, accessible pricing page with monthly/yearly toggle, three-tier cards, and FAQ section — using Ninna UI components and Tailwind v4.

Pricing page structure

A pricing page needs: a headline, a billing toggle (monthly/yearly), pricing cards with features, and an FAQ section. Use Container for max-width, SimpleGrid for the card layout, and Accordion for the FAQ.

1. Monthly/yearly toggle

Use Switch from @ninna-ui/forms for the billing toggle:

import { Switch } from "@ninna-ui/forms";
import { Text, Badge } from "@ninna-ui/primitives";
import { useState } from "react";

const [yearly, setYearly] = useState(false);

<div className="flex items-center justify-center gap-3 mb-8">
  <Text>Monthly</Text>
  <Switch checked={yearly} onCheckedChange={setYearly} />
  <Text>Yearly <Badge variant="soft" color="success" size="sm">Save 20%</Badge></Text>
</div>

2. Three-tier pricing cards

Use Card from @ninna-ui/data-display with a grid layout:

import { Card } from "@ninna-ui/data-display";
import { Button, Heading, Text, Badge } from "@ninna-ui/primitives";
import { VStack } from "@ninna-ui/layout";

<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
  {plans.map((plan) => (
    <Card key={plan.name} className={`p-8 ${plan.popular ? "border-primary ring-2 ring-primary/20" : ""}`}>
      <VStack gap="4">
        {plan.popular && <Badge variant="solid" color="primary">Most Popular</Badge>}
        <Heading as="h3" size="xl">{plan.name}</Heading>
        <Text size="3xl" weight="bold">${yearly ? plan.yearly : plan.monthly}<span className="text-base font-normal text-base-content/60">/mo</span></Text>
        <Button color={plan.popular ? "primary" : "outline"} fullWidth>Get Started</Button>
      </VStack>
    </Card>
  ))}
</div>

3. FAQ with structured data

Use Accordion from @ninna-ui/navigation and inject FAQPage JSON-LD schema for Google rich results:

import { Accordion } from "@ninna-ui/navigation";
import { buildFAQPageJsonLd } from "~/utils/structuredData";

<Accordion type="single" collapsible>
  <Accordion.Item value="q1">
    <Accordion.Trigger>Can I change plans anytime?</Accordion.Trigger>
    <Accordion.Content>Yes, upgrade or downgrade at any time.</Accordion.Content>
  </Accordion.Item>
</Accordion>

<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: buildFAQPageJsonLd(faqs) }} />

Related Blocks

Component Docs

FAQ