Staaarter

Billing Gift Subscription

A gifting form for letting one user pay for another user's subscription. Duration is picked from a row of selectable cards with one-time prices, there's a recipient email field and a personal message textarea with a live character count, and the price preview at the bottom always matches the selected duration. Built for consumer products, educational platforms, and pro-tool upgrades where gifting is a growth lever.

By Staaarter Team
Billing
Updated July 9, 2026
Docs
Live preview

Docs

Installation

A real shadcn registry command, not a copy-paste stand-in: it fetches this block plus its button, input, label, textarea dependencies and any missing npm packages, and installs them straight into your project.

npx shadcn add https://staaarter.com/r/billing-gift-subscription.json

Prefer not to use the CLI? Copy the source from the Code toggle above into src/components/blocks/billing/billing62.tsx instead.

Usage

The file also exports billing62Demo, the exact props behind the preview above. Spread it to get a working section in one line, then replace it with your own data.

import { Billing62, billing62Demo } from "@/components/blocks/billing/billing62";

export default function Page() {
  return <Billing62 {...billing62Demo} />;
}

Props

PropTypeDefaultDescription
headingstring"Gift a subscription"Card heading.
descriptionstringnoneOptional supporting text under the heading.
durationsGiftDuration[]noneDuration options rendered as selectable cards, in the order given.
defaultDurationIdstringdurations[0].idWhich duration is selected on first render.
recipientLabelstring"Recipient email"Label for the recipient email field.
recipientPlaceholderstring"friend@example.com"Placeholder for the recipient email field.
messageLabelstring"Personal message"Label for the message textarea.
messagePlaceholderstringnonePlaceholder for the message textarea.
maxMessageLengthnumber200Character limit enforced on the message field and shown in the counter.
ctaLabelstring"Send gift"Label on the submit button.
classNamestringnoneExtra classes for the outer container.

Types

interface GiftDuration {
  id: string;
  label: string;
  price: number;
}

Behavior notes

  • Selected duration and the message text are both local state; the price preview at the bottom always reflects the currently selected duration.
  • The message textarea is capped at maxMessageLength via the native maxLength attribute, and the counter above it updates as the user types.
  • The recipient email field is a plain uncontrolled input; read its value from the surrounding form on submit rather than from component state, since it isn't tracked internally.
  • Duration cards are real buttons in a radiogroup, so switching duration works with keyboard navigation and screen readers, not only pointer clicks.
  • The submit button doesn't call anything on its own. Wrap the fields in a form and handle submission yourself, or lift state up if you need the selected duration and message outside the component.

Frequently asked questions