# Billing Settings URL: /docs/components/billing-setting The Billing Settings component provides a user-friendly interface for managing billing preferences, payment methods, invoices, and usage limits. This component features a tabbed navigation system, form controls, and an interactive payment card management section. *** title: Billing Settings description: The Billing Settings component provides a user-friendly interface for managing billing preferences, payment methods, invoices, and usage limits. This component features a tabbed navigation system, form controls, and an interactive payment card management section. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ import BillingSettingsDemo from "src/components/billing-settings-demo.tsx" ```tsx title="src/components/billing-settings-demo.tsx" "use client"; import { useState } from "react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Toaster, toast } from "sonner"; import { BillingSettings } from "@/components/billingsdk/billing-settings"; import { CreditCard } from "lucide-react"; type InvoiceFormat = "PDF" | "HTML"; interface Card { id: string; last4: string; brand: "Visa" | "MasterCard" | "Amex" | "Other"; expiry: string; primary: boolean; } interface NewCardForm { number: string; expiry: string; cvc: string; } export default function BillingSettingsDemo() { const [activeTab, setActiveTab] = useState< "general" | "payment" | "invoices" | "limits" >("general"); const [emailNotifications, setEmailNotifications] = useState(true); const [usageAlerts, setUsageAlerts] = useState(true); const [invoiceReminders, setInvoiceReminders] = useState(false); const [cards, setCards] = useState([ { id: "1", last4: "4242", brand: "Visa", expiry: "12/25", primary: true }, ]); const [invoiceFormat, setInvoiceFormat] = useState("PDF"); const [overageProtection, setOverageProtection] = useState(true); const [usageLimitAlerts, setUsageLimitAlerts] = useState(true); const [newCard, setNewCard] = useState({ number: "", expiry: "", cvc: "", }); const [open, setOpen] = useState(false); const handleToggleEmailNotifications = (checked: boolean) => { console.log(`Email notifications toggled to: ${checked}`); setEmailNotifications(checked); }; const handleToggleUsageAlerts = (checked: boolean) => { console.log(`Usage alerts toggled to: ${checked}`); setUsageAlerts(checked); }; const handleToggleInvoiceReminders = (checked: boolean) => { console.log(`Invoice reminders toggled to: ${checked}`); setInvoiceReminders(checked); }; const handleChangeInvoiceFormat = (format: InvoiceFormat) => { console.log(`Invoice format changed to: ${format}`); setInvoiceFormat(format); }; const handleEditBillingAddress = () => { console.log("Edit billing address button clicked"); toast.info("Edit billing address clicked!"); }; const handleToggleOverageProtection = (checked: boolean) => { console.log(`Overage protection toggled to: ${checked}`); setOverageProtection(checked); }; const handleToggleUsageLimitAlerts = (checked: boolean) => { console.log(`Usage limit alerts toggled to: ${checked}`); setUsageLimitAlerts(checked); }; const isValidCardNumber = (number: string): boolean => { const normalized = number.replace(/\s/g, ""); return ( normalized.length >= 13 && normalized.length <= 19 && /^\d+$/.test(normalized) ); }; const isValidExpiry = (expiry: string): boolean => { if (!/^(0[1-9]|1[0-2])\/?([0-9]{2})$/.test(expiry)) { return false; } const [month, year] = expiry.split("/").map(Number); const currentYear = Number(String(new Date().getFullYear()).slice(-2)); const currentMonth = new Date().getMonth() + 1; if (year < currentYear || (year === currentYear && month < currentMonth)) { return false; } return true; }; const isValidCvc = (cvc: string): boolean => { return /^\d{3,4}$/.test(cvc); }; const detectCardBrand = (number: string): Card["brand"] => { if (number.startsWith("4")) return "Visa"; if (/^5[1-5]/.test(number)) return "MasterCard"; if (/^3[47]/.test(number)) return "Amex"; return "Other"; }; const handleAddCard = (): void => { if (!isValidCardNumber(newCard.number)) { toast.error("Please enter a valid card number."); return; } if (!isValidExpiry(newCard.expiry)) { toast.error( "Please enter a valid expiry date (MM/YY) that's not in the past.", ); return; } if (!isValidCvc(newCard.cvc)) { toast.error("Please enter a valid CVC."); return; } const last4 = newCard.number.slice(-4); const brand = detectCardBrand(newCard.number); const newCardData: Card = { id: String(cards.length + 1), last4, brand, expiry: newCard.expiry, primary: cards.length === 0, }; setCards([...cards, newCardData]); setNewCard({ number: "", expiry: "", cvc: "" }); setOpen(false); toast.success("Card added successfully!"); }; const formatCardNumber = (value: string): string => { const rawValue = value.replace(/\D/g, ""); const formattedValue = rawValue.match(/.{1,4}/g)?.join(" ") || ""; return formattedValue; }; return ( setActiveTab(tab as "general" | "payment" | "invoices" | "limits") } emailNotifications={emailNotifications} onEmailNotificationsChange={handleToggleEmailNotifications} usageAlerts={usageAlerts} onUsageAlertsChange={handleToggleUsageAlerts} invoiceReminders={invoiceReminders} onInvoiceRemindersChange={handleToggleInvoiceReminders} cards={cards} onAddCard={() => setOpen(true)} invoiceFormat={invoiceFormat} onInvoiceFormatChange={handleChangeInvoiceFormat} onEditBillingAddress={handleEditBillingAddress} overageProtection={overageProtection} onOverageProtectionChange={handleToggleOverageProtection} usageLimitAlerts={usageLimitAlerts} onUsageLimitAlertsChange={handleToggleUsageLimitAlerts} /> Add Payment Card Enter your card details to add a new payment method Card Number { const rawValue = e.target.value.replace(/\s/g, ""); if (rawValue.length <= 19) { setNewCard({ ...newCard, number: rawValue }); } }} placeholder="1234 5678 9012 3456" className="pr-10" maxLength={19} /> Expiry Date { let value = e.target.value.replace(/\D/g, ""); if (value.length >= 2) { value = value.slice(0, 2) + "/" + value.slice(2, 4); } if (value.length <= 5) { setNewCard({ ...newCard, expiry: value }); } }} placeholder="MM/YY" maxLength={5} /> CVC { const value = e.target.value.replace(/\D/g, ""); if (value.length <= 4) { setNewCard({ ...newCard, cvc: value }); } }} placeholder="123" maxLength={4} /> setOpen(false)}> Cancel Add Card ); } ``` ### Installation ```bash npx shadcn@latest add @billingsdk/billing-settings ``` ```bash pnpm dlx shadcn@latest add @billingsdk/billing-settings ``` ```bash yarn dlx shadcn@latest add @billingsdk/billing-settings ``` ```bash bunx shadcn@latest add @billingsdk/billing-settings ``` ```bash npx @billingsdk/cli add billing-settings ``` ```bash pnpm dlx @billingsdk/cli add billing-settings ``` ```bash yarn dlx @billingsdk/cli add billing-settings ``` ```bash bunx @billingsdk/cli add billing-settings ``` ## Usage ```tsx import { BillingSettings } from "@/components/billingsdk/billing-settings"; ``` ```tsx const [activeTab, setActiveTab] = useState("general") const [emailNotifications, setEmailNotifications] = useState(true) const [usageAlerts, setUsageAlerts] = useState(true) const [invoiceReminders, setInvoiceReminders] = useState(false) const [cards, setCards] = useState([{ id: "1", last4: "4242", brand: "Visa", expiry: "12/25", primary: true }]) const [invoiceFormat, setInvoiceFormat] = useState("PDF") const [overageProtection, setOverageProtection] = useState(true) const [usageLimitAlerts, setUsageLimitAlerts] = useState(true) ``` ```tsx console.log("Add card clicked")} invoiceFormat={invoiceFormat} onInvoiceFormatChange={setInvoiceFormat} onEditBillingAddress={() => console.log("Edit billing address clicked")} overageProtection={overageProtection} onOverageProtectionChange={setOverageProtection} usageLimitAlerts={usageLimitAlerts} onUsageLimitAlertsChange={setUsageLimitAlerts} /> ``` ## Props | Prop | Type | Description | | ---------------------------- | ----------------------------------- | ----------------------------------------------------------------------------- | | `activeTab` | `String` | The currently active tab (`"general"`, `"payment"`, `"invoices"`, `"limits"`) | | `onTabChange` | `(tab: string) => void` | Callback function for when a tab is changed | | `emailNotifications` | `boolean` | State for email notifications switch | | `onEmailNotificationsChange` | `(value: boolean) => void` | Callback for email notifications switch change | | `usageAlerts` | `boolean` | State for usage alerts switch | | `onUsageAlertsChange` | `(value: boolean) => void` | Callback for usage alerts switch change | | `invoiceReminders` | `boolean` | State for invoice reminders switch | | `onInvoiceRemindersChange` | `(value: boolean) => void` | Callback for invoice reminders switch change | | `cards` | `CardInfo[]` | An array of payment card objects | | `onAddCard` | `() => void` | Callback for the "Add new card" button | | `invoiceFormat` | `"PDF" \| "HTML"` | The current invoice format | | `onInvoiceFormatChange` | `(format: "PDF" \| "HTML") => void` | Callback for invoice format change | | `onEditBillingAddress` | `() => void` | Callback for the "Edit billing address" button | | `overageProtection` | `boolean` | State for overage protection switch | | `onOverageProtectionChange` | `(value: boolean) => void` | Callback for overage protection switch change | | `usageLimitAlerts` | `boolean` | State for usage limit alerts switch | | `onUsageLimitAlertsChange` | `(value: boolean) => void` | Callback for usage limit alerts switch change | ## Features * **Tabbed Navigation**: Easily switch between different billing sections. * **Payment Method Management**: View and add credit cards with basic validation. * **Toggle Settings**: Control various notification and protection settings with switches. * **Customizable Invoices**: Option to choose between PDF and HTML invoice formats. * **Responsive Design**: Adapts to different screen sizes for a seamless user experience. ## Theming The pricing table component is styled using the `shadcn/ui` library. You can customize the colors and fonts by overriding the CSS variables. You can also get the theme from the [Theming](/docs/theming) page. ## Example ```tsx title="src/components/billing-settings-demo.tsx" "use client"; import { useState } from "react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Toaster, toast } from "sonner"; import { BillingSettings } from "@/components/billingsdk/billing-settings"; import { CreditCard } from "lucide-react"; type InvoiceFormat = "PDF" | "HTML"; interface Card { id: string; last4: string; brand: "Visa" | "MasterCard" | "Amex" | "Other"; expiry: string; primary: boolean; } interface NewCardForm { number: string; expiry: string; cvc: string; } export default function BillingSettingsDemo() { const [activeTab, setActiveTab] = useState< "general" | "payment" | "invoices" | "limits" >("general"); const [emailNotifications, setEmailNotifications] = useState(true); const [usageAlerts, setUsageAlerts] = useState(true); const [invoiceReminders, setInvoiceReminders] = useState(false); const [cards, setCards] = useState([ { id: "1", last4: "4242", brand: "Visa", expiry: "12/25", primary: true }, ]); const [invoiceFormat, setInvoiceFormat] = useState("PDF"); const [overageProtection, setOverageProtection] = useState(true); const [usageLimitAlerts, setUsageLimitAlerts] = useState(true); const [newCard, setNewCard] = useState({ number: "", expiry: "", cvc: "", }); const [open, setOpen] = useState(false); const handleToggleEmailNotifications = (checked: boolean) => { console.log(`Email notifications toggled to: ${checked}`); setEmailNotifications(checked); }; const handleToggleUsageAlerts = (checked: boolean) => { console.log(`Usage alerts toggled to: ${checked}`); setUsageAlerts(checked); }; const handleToggleInvoiceReminders = (checked: boolean) => { console.log(`Invoice reminders toggled to: ${checked}`); setInvoiceReminders(checked); }; const handleChangeInvoiceFormat = (format: InvoiceFormat) => { console.log(`Invoice format changed to: ${format}`); setInvoiceFormat(format); }; const handleEditBillingAddress = () => { console.log("Edit billing address button clicked"); toast.info("Edit billing address clicked!"); }; const handleToggleOverageProtection = (checked: boolean) => { console.log(`Overage protection toggled to: ${checked}`); setOverageProtection(checked); }; const handleToggleUsageLimitAlerts = (checked: boolean) => { console.log(`Usage limit alerts toggled to: ${checked}`); setUsageLimitAlerts(checked); }; const isValidCardNumber = (number: string): boolean => { const normalized = number.replace(/\s/g, ""); return ( normalized.length >= 13 && normalized.length <= 19 && /^\d+$/.test(normalized) ); }; const isValidExpiry = (expiry: string): boolean => { if (!/^(0[1-9]|1[0-2])\/?([0-9]{2})$/.test(expiry)) { return false; } const [month, year] = expiry.split("/").map(Number); const currentYear = Number(String(new Date().getFullYear()).slice(-2)); const currentMonth = new Date().getMonth() + 1; if (year < currentYear || (year === currentYear && month < currentMonth)) { return false; } return true; }; const isValidCvc = (cvc: string): boolean => { return /^\d{3,4}$/.test(cvc); }; const detectCardBrand = (number: string): Card["brand"] => { if (number.startsWith("4")) return "Visa"; if (/^5[1-5]/.test(number)) return "MasterCard"; if (/^3[47]/.test(number)) return "Amex"; return "Other"; }; const handleAddCard = (): void => { if (!isValidCardNumber(newCard.number)) { toast.error("Please enter a valid card number."); return; } if (!isValidExpiry(newCard.expiry)) { toast.error( "Please enter a valid expiry date (MM/YY) that's not in the past.", ); return; } if (!isValidCvc(newCard.cvc)) { toast.error("Please enter a valid CVC."); return; } const last4 = newCard.number.slice(-4); const brand = detectCardBrand(newCard.number); const newCardData: Card = { id: String(cards.length + 1), last4, brand, expiry: newCard.expiry, primary: cards.length === 0, }; setCards([...cards, newCardData]); setNewCard({ number: "", expiry: "", cvc: "" }); setOpen(false); toast.success("Card added successfully!"); }; const formatCardNumber = (value: string): string => { const rawValue = value.replace(/\D/g, ""); const formattedValue = rawValue.match(/.{1,4}/g)?.join(" ") || ""; return formattedValue; }; return ( setActiveTab(tab as "general" | "payment" | "invoices" | "limits") } emailNotifications={emailNotifications} onEmailNotificationsChange={handleToggleEmailNotifications} usageAlerts={usageAlerts} onUsageAlertsChange={handleToggleUsageAlerts} invoiceReminders={invoiceReminders} onInvoiceRemindersChange={handleToggleInvoiceReminders} cards={cards} onAddCard={() => setOpen(true)} invoiceFormat={invoiceFormat} onInvoiceFormatChange={handleChangeInvoiceFormat} onEditBillingAddress={handleEditBillingAddress} overageProtection={overageProtection} onOverageProtectionChange={handleToggleOverageProtection} usageLimitAlerts={usageLimitAlerts} onUsageLimitAlertsChange={handleToggleUsageLimitAlerts} /> Add Payment Card Enter your card details to add a new payment method Card Number { const rawValue = e.target.value.replace(/\s/g, ""); if (rawValue.length <= 19) { setNewCard({ ...newCard, number: rawValue }); } }} placeholder="1234 5678 9012 3456" className="pr-10" maxLength={19} /> Expiry Date { let value = e.target.value.replace(/\D/g, ""); if (value.length >= 2) { value = value.slice(0, 2) + "/" + value.slice(2, 4); } if (value.length <= 5) { setNewCard({ ...newCard, expiry: value }); } }} placeholder="MM/YY" maxLength={5} /> CVC { const value = e.target.value.replace(/\D/g, ""); if (value.length <= 4) { setNewCard({ ...newCard, cvc: value }); } }} placeholder="123" maxLength={4} /> setOpen(false)}> Cancel Add Card ); } ``` ### Credits * Created by [@shashwat558](https://github.com/shashwat558)