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) }} />