Custom Pricing
extends regular pricing plans, with more flexibilty that allow to add custom features with custom pricing.
Loading...
Installation
Usage
for one select (radio input) use <CustomPricingCheckedControl />
function SelectItem({
id,
label,
price,
checked,
handleChange,
groupId,
}: {
id: string;
label: string;
price: string;
checked: boolean;
handleChange: () => void;
groupId: string;
}) {
return (
<div className="relative group rounded-full transition-colors duration-200 has-[:checked]:text-white grid grid-cols-1 grid-rows-1 *:col-start-1 *:row-start-1">
<input
className="size-full [all:unset]"
type="checkbox"
name={id}
value={id}
id={id}
checked={checked}
onChange={handleChange}
/>
<label
htmlFor={id}
className="relative p-2 z-2 text-nowrap pointer-events-none flex gap-px flex-wrap justify-center items-center text-xs font-medium"
>
{label} <span className="text-[10px] tabular-nums">{price}</span>
</label>
{checked && (
<motion.div
layoutId={groupId}
className="size-full rounded-full"
style={{
background: 'var(--primary)',
backgroundImage: 'linear-gradient(180deg, #444 0%, #000 100%)',
}}
transition={{
type: 'spring',
stiffness: 260,
damping: 20,
mass: 1,
}}
/>
)}
</div>
);
}
function Selects() {
return (
<div
className="relative flex bg-input rounded-full ring ring-ring/40 p-0.5 w-fit border"
id="custom-pricing-selects"
>
<CustomPricingCheckedControl
id="landing page"
defaultChecked={true}
price={1500}
group="website-type"
render={({ id, checked, toggleChecked }) => (
<div className="relative ">
<SelectItem
price={'$1.500'}
id={id}
checked={checked}
handleChange={() => toggleChecked(id)}
label="Landing page"
groupId="custom-pricing-selects"
/>
</div>
)}
/>
<CustomPricingCheckedControl
id="business website"
price={3000}
group="website-type"
render={({ id, checked, toggleChecked }) => (
<div className="relative ">
<SelectItem
price={'$3.000'}
id={id}
checked={checked}
handleChange={() => toggleChecked(id)}
label="Business website"
groupId="custom-pricing-selects"
/>
</div>
)}
/>
</div>
);
}for toggle checks use <CustomPricingCheckedControl /> with type="checkbox"
<CustomPricingCheckedControl
id="seo-optimization"
price={200}
type="checkbox"
render={({ id, checked, toggleChecked }) => (
<div className="flex items-center justify-between p-3 rounded-xl border bg-card/50 transition-colors hover:bg-card/80">
<div className="space-y-0.5">
<Label
htmlFor={id}
className="text-sm font-medium leading-none cursor-pointer"
>
SEO Optimization
</Label>
</div>
<div className="flex items-center gap-3">
<span className="text-xs font-medium tabular-nums">$200</span>
<Switch
checked={checked}
onCheckedChange={() => toggleChecked(id)}
id={id}
/>
</div>
</div>
)}
/>for quantity controls use <CustomPricingQuantityControl />
<CustomPricingQuantityControl
id="number of pages"
unitPrice={100}
render={({ id, subtotal, quantity, handleChange }) => (
<div className="space-y-1">
<div className="flex items-center gap-2 justify-between">
<Label className="text-sm font-medium" htmlFor={id}>
Number of pages
</Label>
<SlidingNumber
value={subtotal}
className="text-xs font-medium text-muted-foreground tracking-tight"
/>
</div>
<Slider
id={id}
min={0}
max={10}
step={1}
value={[quantity]}
onValueChange={handleChange}
/>
</div>
)}
/>for total controls use <CustomPricingTotalControl />
<CustomPricingTotalControl
render={({ total }) => (
<span className="tabular-nums font-semibold">${total}</span>
)}
/>Props
CustomPricingQuantityControl
| Prop | Type | Default |
|---|---|---|
id | string | - |
render | { id: string, unitPrice: number, subtotal: number, quantity: number, updateQuantity: (id: string, quantity: number) => void, handleChange: (val: number[]) => void } | - |
unitPrice? | number | - |
defaultValue? | number | - |
CustomPricingCheckedControl
| Prop | Type | Default |
|---|---|---|
id | string | - |
render | { id: string, checked: boolean, price: number, toggleChecked: (id: string) => void } | - |
type? | checkbox | radio | - |
group? | string | - |
CustomPricingTotalControl
| Prop | Type | Default |
|---|---|---|
render | { total: number } | - |