Payment Card
- Stable
- WCAG 2.2 evidence
- RTL
A payment card: Style x Side, with an optional Frozen overlay, Virtual tag, chip and contactless
symbol. Presentational: renders in React Server Components, no React Aria. Card data is handled with care: the number is **masked by default** and only its last four digits are read, so nothing
else can reach the markup, the accessible name or a console. The card is a named group ("Acme Corporate, card ending
4417"); the visible masked digits are hidden from assistive technology so they are not read as bullets. The number, expiry
and security code placeholder stay left to right in right-to-left layouts. Frozen is a visible word with a snowflake, not
only a frosted fill (WCAG 1.4.1). Decoration (rings, chip, strip, signature panel) is aria-hidden (1.1.1). The library draws no third-party brand marks: the network logo is a slot. Consumer duties: reveal flows and who may start
them, never logging or persisting card data, the alt text of the logo, and wording of the note.
import { PaymentCard } from "@nexera-ui/react";- 10
- examples
- 14
- props
- 7
- live controls
- 1
- platform
- 6
- WCAG criteria
- 2
- blocks use it
Try every prop. Copy the code.
Change the props and the code updates. Check light and dark, LTR and RTL, and phone width.
import { PaymentCard } from "@nexera-ui/react";
<PaymentCard />Examples 9
The same examples as Storybook, rendered live. Open Code to copy one.
Back
The back: magnetic strip, signature panel, a masked security code (never shown) and the note.
import { PaymentCard } from "@nexera-ui/react";
function NetworkPlaceholder() {
return (
<svg role="img" aria-label="Card network" viewBox="0 0 52 32" className="size-full">
<rect x="0.5" y="0.5" width="51" height="31" rx="5.5" fill="none" stroke="currentColor" />
<text x="26" y="20" textAnchor="middle" fontSize="11" fill="currentColor">
NET
</text>
</svg>
);
}
<PaymentCard
cardName="Acme Corporate"
holder="AYESHA KHAN"
expiry="09/29"
networkLogo={<NetworkPlaceholder />}
side="back"
note="Issued by Acme Pakistan. If found, return to any branch."
/>;
Frozen
Figma Frozen: a frosted overlay with a snowflake and the word "Frozen".
import { PaymentCard } from "@nexera-ui/react";
function NetworkPlaceholder() {
return (
<svg role="img" aria-label="Card network" viewBox="0 0 52 32" className="size-full">
<rect x="0.5" y="0.5" width="51" height="31" rx="5.5" fill="none" stroke="currentColor" />
<text x="26" y="20" textAnchor="middle" fontSize="11" fill="currentColor">
NET
</text>
</svg>
);
}
<PaymentCard
cardName="Acme Corporate"
holder="AYESHA KHAN"
expiry="09/29"
networkLogo={<NetworkPlaceholder />}
isFrozen
/>;
Virtual
Figma Virtual: a "Virtual" tag next to the card name.
import { PaymentCard } from "@nexera-ui/react";
function NetworkPlaceholder() {
return (
<svg role="img" aria-label="Card network" viewBox="0 0 52 32" className="size-full">
<rect x="0.5" y="0.5" width="51" height="31" rx="5.5" fill="none" stroke="currentColor" />
<text x="26" y="20" textAnchor="middle" fontSize="11" fill="currentColor">
NET
</text>
</svg>
);
}
<PaymentCard
cardName="Acme Corporate"
holder="AYESHA KHAN"
expiry="09/29"
networkLogo={<NetworkPlaceholder />}
isVirtual
/>;
Without chip
hasChip and hasContactless off, for a virtual card.
import { PaymentCard } from "@nexera-ui/react";
function NetworkPlaceholder() {
return (
<svg role="img" aria-label="Card network" viewBox="0 0 52 32" className="size-full">
<rect x="0.5" y="0.5" width="51" height="31" rx="5.5" fill="none" stroke="currentColor" />
<text x="26" y="20" textAnchor="middle" fontSize="11" fill="currentColor">
NET
</text>
</svg>
);
}
<PaymentCard
cardName="Acme Corporate"
holder="AYESHA KHAN"
expiry="09/29"
networkLogo={<NetworkPlaceholder />}
isVirtual
/>;
Reveal flow
A reveal flow: the number is masked until the user asks for it. The whole number is only in the markup while it is revealed, and the accessible name always says "card ending 4417". Never show full numbers outside a flow like this.
import { useState } from "react";
import { Button, PaymentCard } from "@nexera-ui/react";
function NetworkPlaceholder() {
return (
<svg role="img" aria-label="Card network" viewBox="0 0 52 32" className="size-full">
<rect x="0.5" y="0.5" width="51" height="31" rx="5.5" fill="none" stroke="currentColor" />
<text x="26" y="20" textAnchor="middle" fontSize="11" fill="currentColor">
NET
</text>
</svg>
);
}
export function RevealFlow() {
const [revealed, setRevealed] = useState(false);
return (
<div className="flex flex-col items-start gap-4">
<PaymentCard
cardName="Acme Corporate"
holder="AYESHA KHAN"
expiry="09/29"
networkLogo={<NetworkPlaceholder />}
isRevealed={revealed}
/>
<Button
variant="secondary"
onPress={() => {
setRevealed((value) => !value);
}}
>
{revealed ? "Hide number" : "Reveal number"}
</Button>
</div>
);
}
Fifteen digits
American Express numbers have 15 digits grouped 4-6-5.
import { PaymentCard } from "@nexera-ui/react";
function NetworkPlaceholder() {
return (
<svg role="img" aria-label="Card network" viewBox="0 0 52 32" className="size-full">
<rect x="0.5" y="0.5" width="51" height="31" rx="5.5" fill="none" stroke="currentColor" />
<text x="26" y="20" textAnchor="middle" fontSize="11" fill="currentColor">
NET
</text>
</svg>
);
}
<PaymentCard
cardName="Acme Travel"
holder="AYESHA KHAN"
expiry="09/29"
networkLogo={<NetworkPlaceholder />}
number="3782 822463 10005"
/>;
Long content
Long names and holders wrap instead of truncating; the card keeps its Figma width and grows in height.
import { PaymentCard } from "@nexera-ui/react";
function NetworkPlaceholder() {
return (
<svg role="img" aria-label="Card network" viewBox="0 0 52 32" className="size-full">
<rect x="0.5" y="0.5" width="51" height="31" rx="5.5" fill="none" stroke="currentColor" />
<text x="26" y="20" textAnchor="middle" fontSize="11" fill="currentColor">
NET
</text>
</svg>
);
}
<PaymentCard
cardName="Acme Corporate Travel and Expenses Programme for Field Staff"
holder="AYESHA KHAN-MALIK DE LA CRUZ"
expiry="09/29"
networkLogo={<NetworkPlaceholder />}
isVirtual
/>;
Right to left
Right-to-left (Arabic): the layout mirrors, but the number and the expiry stay left to right.
import { PaymentCard } from "@nexera-ui/react";
function NetworkPlaceholder() {
return (
<svg role="img" aria-label="Card network" viewBox="0 0 52 32" className="size-full">
<rect x="0.5" y="0.5" width="51" height="31" rx="5.5" fill="none" stroke="currentColor" />
<text x="26" y="20" textAnchor="middle" fontSize="11" fill="currentColor">
NET
</text>
</svg>
);
}
<PaymentCard
cardName="أكمي للشركات"
holder="عائشة خان"
expiry="09/29"
networkLogo={<NetworkPlaceholder />}
isVirtual
labels={{
cardHolder: "اسم حامل البطاقة",
expires: "تنتهي في",
virtual: "افتراضية",
endingIn: (last) => `بطاقة تنتهي بـ ${last}`,
}}
/>;
Narrow container
In a narrow container the card shrinks below its 340 px width and the number groups wrap.
import { PaymentCard } from "@nexera-ui/react";
function NetworkPlaceholder() {
return (
<svg role="img" aria-label="Card network" viewBox="0 0 52 32" className="size-full">
<rect x="0.5" y="0.5" width="51" height="31" rx="5.5" fill="none" stroke="currentColor" />
<text x="26" y="20" textAnchor="middle" fontSize="11" fill="currentColor">
NET
</text>
</svg>
);
}
<div className="max-w-64 border border-dashed border-default p-2">
<PaymentCard
cardName="Acme Corporate"
holder="AYESHA KHAN"
expiry="09/29"
networkLogo={<NetworkPlaceholder />}
/>
</div>;
Props 14
Press "Try it" on a card to load that prop into the playground.
14 props shown
variantNexera"emerald" | "midnight" | "light"Colour. Named variant, the library-wide name, because style is the React
inline-style prop.
sideNexera"front" | "back"Which face is shown. The back has the magnetic strip, the signature panel, a masked security code and the
note. The security code is never shown.
cardNameNexerastringName of the card. Shown on the front and part of the accessible name.
holderNexerastringCardholder. Shown as typed.
numberNexerastringThe card number. Only digits count; **only the last four digits are ever read from it**: by default the card shows the number masked ("•••• •••• •••• 4417") and the other digits are never rendered, so they cannot reach the DOM, the accessible name or a log. Pass the already-masked number or just the last four digits if you do not want the full number in your props either. It is always left to right.
isRevealedNexerabooleanShow the whole number (a reveal flow the user started, for example after a re-authentication). The accessible name still only says "card ending 1234". Never show full numbers outside such a flow.
expiryNexerastringExpiry. Shown as typed, left to right.
networkLogoNexeraReactNodeThe network's mark, 52 x 32 px. The library draws no brand marks: pass your own SVG or image (decorative or with its own alt text).
isFrozenNexerabooleanThe card is frozen: a frosted overlay with a "Frozen" badge. The word is text, announced with the card.
isVirtualNexerabooleanThe card is virtual: a "Virtual" tag next to the name.
hasChipNexerabooleanDraw the EMV chip. Front only. Figma has this as a boolean with no content to swap, so it is a boolean here and not the slot the plan proposed.
hasContactlessNexerabooleanDraw the contactless symbol. Front only.
noteNexerastringFine print on the back.
labelsNexeraPartial<PaymentCardLabels>Translate the words the card adds.
* Required. React Aria props shown are the ones most apps use; the component accepts the rest of its React Aria props too.
Accessible by default.
Built on React Aria, and covered by the WCAG 2.2 evidence generated on every build.
WCAG 2.2 evidence
4 direct · 2 supporting- 1.1.1Non-text ContentLevel A · tested directly
- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.4.1Use of ColorLevel A · tested directly
- 1.4.3Contrast (Minimum)Level AA · supporting test
- 1.4.10ReflowLevel AA · supporting test
- 4.1.2Name, Role, ValueLevel A · tested directly
Fits any width.
Nexera components respond to the space they are given. Drag the corner of the frame, or pick a width.
Styling hooks
Pass className to add Tailwind classes (merged last). State is exposed as data attributes, so you can style it with variants like data-pressed:.
<PaymentCard className="shadow-sm" />Used in blocks
Related components
- AccordionItemOne collapsible section of an FAQ or settings page.
- AreaChartA stacked area chart: layers piled up over time with the title, range switch, actions and legend of the card.
- AvailabilityRowOne person's availability across a window of the day: an avatar with initials and the name, and a timeline with the busy periods drawn as bars.
- AvatarA person or organisation shown as a photo, initials or an icon.
- AvatarGroupA row of overlapping avatars with a "+N" chip for the rest.
- AvatarLabelAn avatar with a name and an optional subtitle beside it, for a person in a list, a header or a mention.
- BadgeShort status or category label: `Tone` x `Variant` x `Size`, with an optional dot, leading icon and remove button.
- BadgeGroupA pill that pairs a small badge with a short message, for an announcement or a status line.