@xsolla/xui-input-copy
v0.187.3
Published
A cross-platform React input component that includes a copy-to-clipboard button. Supports visibility toggle for sensitive data like API keys or tokens. <!-- BEGIN:xui-mcp-instructions:input-copy --> An input field that displays a value and provides a one-
Readme
Input Copy
A cross-platform React input component that includes a copy-to-clipboard button. Supports visibility toggle for sensitive data like API keys or tokens.
An input field that displays a value and provides a one-click copy-to-clipboard action. The copy button is built into the right side of the field — clicking it copies the displayed text and transitions the component to State=Copied to confirm the action. Supports five sizes, an optional leading icon, a visibility toggle for sensitive values, and an error state.
When to use
When a value must be both readable and copyable — API keys, access tokens, referral links, invite codes, wallet addresses, webhook URLs, promo codes
When the user may occasionally need to edit the value in addition to copying it — for example, adjusting a URL before sharing
When copy confirmation matters — the Copied state makes it clear the action succeeded without requiring a Toast notification
In developer portals, settings panels, sharing flows, and payment confirmation screens
When not to use
When the value is purely display-only and never needs to be copied — use a plain text element
When the user should copy but never edit — consider making the field read-only at the product level (not the component level), but InputCopy itself supports editing by default
When multiple copy fields appear in a dense list — consider grouping them under a single copy-all action instead
Behaviour guidelines
Editable value — the field is a standard editable input. The user can click into it, select text, and type. The copy button copies the current value at the time of clicking, including any edits the user has made.
Copy on click — clicking the copy button copies the full Input text value to the clipboard using the Clipboard API. Immediately transition to State=Copied.
Copy on field click — optionally, clicking anywhere on the field (not just the button) can also trigger the copy action, especially on mobile where small buttons are hard to tap accurately. If implemented, the entire field becomes the tap target for copying.
Revert timer — after entering State=Copied, start a timer of 1.5–2 seconds. When the timer expires, revert to State=Default. If the user clicks the copy button again during the Copied state, restart the timer.
Clipboard API fallback — if the Clipboard API is unavailable (e.g. non-HTTPS context, older browser), fall back to document.execCommand('copy') or show a tooltip instructing the user to copy manually (e.g. "Press Ctrl+C to copy"). Do not silently fail.
Truncation — the value text is always single-line and truncated with an ellipsis (text-overflow: ellipsis) when it exceeds the field width. The full value is always available via the copy action, regardless of what is visible. Show the full value in a tooltip on hover when truncation is visible.
Visibility=False + copy — always copy the full unmasked value to the clipboard, even when Visibility=False. Masking is a visual-only feature.
Error state — use State=Error when the value is invalid, expired, or unavailable (e.g. an expired API token, a revoked access key). Show a descriptive error message below the field. The copy button may be disabled or hidden in this state depending on context.
Disable state — use State=Disable when the value cannot be interacted with in the current context (e.g. a feature is not enabled for this account). Show the value in muted style. Do not disable the field when the only difference is that copying is blocked — the field should still be readable.
Mobile — on touch devices, use Visibility=True by default unless the value is genuinely sensitive. Truncation without a hover tooltip is a problem on mobile — consider showing a narrower, more concise value or using a different layout.
Content guidelines
Value text — display the exact value the user needs. Do not add labels or prefixes inside the field (e.g. do not show "key: sk-prod-…" — show only sk-prod-…). The field label above the component communicates what the value is.
Field label — always provide a visible label above the component: "API key", "Referral link", "Access token", "Promo code". Do not rely on the icon or placeholder alone. Error messages — be specific:
- "This token has expired. Generate a new one below."
- "API key is unavailable. Contact your administrator."
- "Link has been revoked."
- Tooltip on copy button — add a tooltip to the copy button with aria-label text: "Copy API key", "Copy link", "Copy code". The tooltip disappears when the state transitions to Copied and the checkmark is shown.
- Copied confirmation text — if additional text confirmation is needed (e.g. a tooltip or an aria-live announcement), use "Copied!" or "Copied to clipboard". Keep it brief.
Accessibility
The field must be a standard element — keyboard-focusable, editable, and selectable. Do not use readonly or disabled at the component level; handle those constraints at the product level if needed.
The copy button must be a element with aria-label identifying what is being copied — e.g. aria-label="Copy API key".
When State=Copied, update the button's aria-label to "Copied" and add aria-pressed="true" or update aria-live so screen readers announce the confirmation.
Use aria-live="polite" on a visually hidden region to announce "Copied to clipboard" when the state transitions to Copied. This ensures screen reader users receive the same confirmation that sighted users see via the checkmark.
When State=Error, the error message must be linked via aria-describedby and placed in an aria-live="polite" region.
When State=Disable, the copy button must have aria-disabled="true" and must not receive focus.
The leading icon must have aria-hidden="true".
When Visibility=False and the value is masked, add a tooltip or aria-description on the copy button clarifying that the full value will be copied despite the masking: e.g. aria-label="Copy secret key (hidden)".
Ensure the copy button meets the minimum touch target of 44 × 44 px on mobile. For XS [32] and S [40] sizes, add layout padding as needed.
Installation
npm install @xsolla/xui-input-copyDemo
Basic Input Copy
import * as React from "react";
import { InputCopy } from "@xsolla/xui-input-copy";
export default function BasicInputCopy() {
return (
<InputCopy
value="sk_live_abc123xyz"
onCopy={(val) => console.log("Copied:", val)}
/>
);
}With Label
import * as React from "react";
import { InputCopy } from "@xsolla/xui-input-copy";
export default function LabeledInputCopy() {
return (
<InputCopy
label="API Key"
value="sk_live_abc123xyz789"
onCopy={(val) => console.log("Copied:", val)}
/>
);
}Secure Text Entry
import * as React from "react";
import { InputCopy } from "@xsolla/xui-input-copy";
export default function SecureInputCopy() {
return (
<InputCopy
label="Secret Token"
value="super_secret_token_12345"
secureTextEntry={true}
onCopy={(val) => console.log("Copied:", val)}
/>
);
}Editable Input Copy
import * as React from "react";
import { InputCopy } from "@xsolla/xui-input-copy";
export default function EditableInputCopy() {
const [value, setValue] = React.useState("editable-value");
return (
<InputCopy
label="Editable Field"
value={value}
onChange={(e) => setValue(e.target.value)}
onCopy={(val) => console.log("Copied:", val)}
/>
);
}Anatomy
import { InputCopy } from "@xsolla/xui-input-copy";
<InputCopy
value="text to copy" // Value to display and copy
onChange={handleChange} // Change event handler
onCopy={handleCopy} // Callback after copying
secureTextEntry={false} // Hide text like password
initialVisible={false} // Initial visibility (when secure)
size="md" // Size variant
label="Label" // Label above input
disabled={false} // Disabled state
error={false} // Error state
errorMessage="Error" // Error message text
/>;Examples
Input Copy Sizes
import * as React from "react";
import { InputCopy } from "@xsolla/xui-input-copy";
export default function InputCopySizes() {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
<InputCopy size="xs" value="Extra Small" />
<InputCopy size="sm" value="Small" />
<InputCopy size="md" value="Medium" />
<InputCopy size="lg" value="Large" />
<InputCopy size="xl" value="Extra Large" />
</div>
);
}Error State
import * as React from "react";
import { InputCopy } from "@xsolla/xui-input-copy";
export default function ErrorInputCopy() {
return (
<InputCopy
label="API Key"
value="invalid-key"
error={true}
errorMessage="Invalid API key format"
/>
);
}API Reference
InputCopy
InputCopy Props:
| Prop | Type | Default | Description |
| :-------------- | :------------------------------------- | :------ | :------------------------------------------------------------------------------------------------------------ |
| testID | string | — | Test ID for testing frameworks. On web this renders as data-testid; on React Native it renders as testID. |
| value | string | "" | Value to display and copy. |
| onChange | (e: ChangeEvent) => void | - | Change event handler. |
| onChangeText | (text: string) => void | - | Text change handler. |
| onCopy | (value: string) => void | - | Callback when value is copied. |
| secureTextEntry | boolean | false | Hide text like a password field. |
| initialVisible | boolean | false | Initial visibility when secureTextEntry is true. |
| size | "xl" \| "lg" \| "md" \| "sm" \| "xs" | "md" | Component size. |
| label | string | - | Label above input. |
| disabled | boolean | false | Disabled state. |
| error | boolean | false | Error state. |
| errorMessage | string | - | Error message text. |
| placeholder | string | - | Placeholder text. |
| testID | string | - | Test identifier. |
Behavior
- Copy Button: Shows checkmark icon briefly (2 seconds) after successful copy
- Visibility Toggle: When
secureTextEntryis enabled, the eye icon reflects the current state:- Open eye = Text is currently visible (type="text")
- Closed eye = Text is currently hidden (type="password")
- This follows modern design system conventions (Material, Apple, Atlassian, Polaris)
- Uses browser Clipboard API for copying
- Error state shows red border and error message
Accessibility
- Copy button has
aria-labelthat changes from "Copy to clipboard" to "Copied" after action - Visibility toggle button has appropriate
aria-label:- "Show text" when text is hidden
- "Hide text" when text is visible
- Toggle button uses
aria-pressedto indicate current visibility state - Error messages linked via
aria-describedbyfor screen reader context - Labels properly linked via
aria-labelledbywhen provided - Input has
aria-invalidwhen in error state - Error messages use
role="alert"for immediate announcement
