Xzero UI
Pro library

PII redactionPro

A guardrail card where personal data is redacted before the prompt reaches the model. Scan marks each name, email, phone number and card with its type and swaps it for a placeholder like [EMAIL_1]. The safe prompt travels to Claude, and the reply comes back with the original values put back for the person.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/pii-redaction
Live · keyboard ready

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/pii-redaction

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { PiiRedaction, detectPii, redactPii, restorePii } from "@/components/xzero/pii-redaction/pii-redaction";

export function SupportGuard({ user }) {
  return (
    <PiiRedaction
      autoPlay={false}
      defaultMessage=""
      names={[user.name]}
      model={{ name: "Claude Sonnet 5.5" }}
      detect={async (text, signal) => {
        const res = await fetch("/api/dlp/scan", { method: "POST", body: text, signal });
        return res.json(); // [{ type: "email", start: 74, end: 100 }, ...]
      }}
      send={async (prompt, signal) => {
        const res = await fetch("/api/chat", { method: "POST", body: JSON.stringify({ prompt }), signal });
        const { text } = await res.json();
        return text; // may contain [NAME_1], [EMAIL_1], restored by the component
      }}
      onRedact={({ entities }) => audit.log("pii.redacted", entities.map(e => e.type))}
    />
  );
}

// Server side, without the card:
const { prompt, entities } = redactPii(message, detectPii(message, { names: [user.name] }));
const reply = restorePii(await model.complete(prompt), entities);

// With no props it plays the simulated support conversation.
<PiiRedaction />

API reference

PiiRedaction

A guardrail card that shows personal data being redacted before a prompt reaches the model. Scan marks each name, email, phone number and card in the message with its type, then swaps each one for a numbered placeholder such as [EMAIL_1] in a single crossfade. Send carries the safe prompt along a small flow (you, the PII guard, the model), the reply streams back in quick chunks with its placeholders, and the guard restores the original values for the person. Show original flips the message between the safe prompt and what was typed. The message is editable before a scan, so people can paste their own text. With no props it plays a simulated support conversation with Claude Sonnet 5.5 on a loop while on screen.

PropTypeDefaultDescription
defaultMessagestringpiiSampleMessageThe message to check. The sample is a double charge support request from Sofia Ramirez with a name, a Luhn-valid test card, an email and a phone number.
editablebooleantrueLets people edit the message before a scan. Cmd or Ctrl plus Enter scans from the field.
namesstring[]["Sofia Ramirez"]Names the built-in detector always flags, on top of introductions like "I'm", "this is" and "my name is". Pass the signed-in person's name and their contacts.
detect(text: string, signal: AbortSignal) => PiiMatch[] | Promise<PiiMatch[]>–Replaces the built-in detector, for example with your DLP or NER service. Return spans as { type: "name" | "email" | "phone" | "card", start, end } with an exclusive end. The signal aborts when the person presses Stop or Reset.
send(prompt: string, signal: AbortSignal) => Promise<string>–Sends the redacted prompt to your model and resolves with its reply, placeholders included. The component restores them. Without it the reply is simulated and the status says so. A rejection shows the error message with Retry.
model{ name: string; mark?: ReactNode }{ name: "Claude Sonnet 5.5" }The model the safe prompt goes to, on the flow and above the reply. The mark draws at 16px; without one the Claude mark shows in its brand color.
autoPlaybooleantruePlays scan, send and restore on a loop while at least a quarter of the card is on screen. Stops for good as soon as someone edits, toggles or presses a button. Off with reduced motion.
showOriginalboolean–Shows the original message instead of the safe prompt once it is redacted. Pair with onShowOriginalChange.
defaultShowOriginalbooleanfalseInitial Show original state when uncontrolled.
onShowOriginalChange(show: boolean) => void–Called when the Show original switch changes.
onRedact(result: { prompt: string; entities: PiiEntity[] }) => void–Called once the message is redacted, with the safe prompt and every entity (type, start, end, value, token). Log the entities server side, never the prompt alongside them.
onReply(reply: { raw: string; restored: string }) => void–Called when the reply is restored, with the model's raw reply and the text the person sees.
onPhaseChange(phase: PiiRedactionPhase) => void–Called on every phase change: idle, scanning, redacted, sending, thinking, streaming, restoring, done or error.
classNamestring–Class on the root card.

detectPii, redactPii, restorePii

The pure helpers behind the card, safe on the server. detectPii(text, { names }) finds emails, card numbers (13 to 19 digits that pass Luhn), phone numbers (7 to 15 digits) and names. redactPii(text, matches) returns { prompt, entities } and gives a repeated value the same token. restorePii(reply, entities) puts the values back and leaves unknown tokens as they are.

PropTypeDefaultDescription
textstring–The message to scan or redact.
matchesPiiMatch[]–Spans from detectPii or your own detector. Overlapping spans after the first are skipped.
entitiesPiiEntity[]–The entities from redactPii, used to restore a reply.

Keyboard interactions

KeysAction
TabMoves through the message field (before a scan), the Show original switch once there is a safe prompt, Reset when it shows, and the primary action.
Cmd + Enter, Ctrl + EnterScans the message from the field. Focus moves to the primary action, which now reads Stop.
Space, EnterToggles Show original, or activates Scan, Stop, Send, Run again, Retry or Reset.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Also in ai

Guidance for AI tools

PII redaction: A guardrail card where personal data is redacted before the prompt reaches the model. Scan marks each name, email, phone number and card with its type and swaps it for a placeholder like [EMAIL_1]. The safe prompt travels to Claude, and the reply comes back with the original values put back for the person. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components