Getting StartedComponentsDesign TokensThemingTheme SynthesizerFrameworksAccessibilityUtilitiesServer RenderingBrowser SupportContributingChangelogApp ShellAspect GridAuth ShellCenterContainerDashboard GridFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbarAnchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkStepper NavTabsTop BarTree ViewAccordionAspect RatioAvatarAvatar GroupCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSkeletonSpinnerStackVideoVirtual ListActivity HeatmapAnimated NumberBadgeBar ListChartClockComparisonCountdown TimerData GridDescription ListDiffGaugeJSON TreeKanbanLevel MeterListMeterSparklineStatStepperTagTimelineUptimeValue CardWaveformBlockquoteCode BlockCode GroupGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriterButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerField ListFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree SelectAlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuHover CardLoading OverlayNotification PanelPopoverProgressSheetToastTooltip
ARC UIARC Radiant Components
v4.9DocsComponentsTokensSynthesizer
Getting StartedFrameworksServer RenderingDesign TokensThemingTheme SynthesizerTypographyUtilitiesAll ComponentsAccessibilityBrowser SupportChangelogContributingStatsApp ShellAspect GridAuth ShellCenterContainerDashboard GridFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbarAnchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkStepper NavTabsTop BarTree ViewAccordionAspect RatioAvatarAvatar GroupCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSkeletonSpinnerStackVideoVirtual ListActivity HeatmapAnimated NumberBadgeBar ListChartClockComparisonCountdown TimerData GridDescription ListDiffGaugeJSON TreeKanbanLevel MeterListMeterSparklineStatStepperTagTimelineUptimeValue CardWaveformBlockquoteCode BlockCode GroupGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriterButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerField ListFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree SelectAlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuHover CardLoading OverlayNotification PanelPopoverProgressSheetToastTooltip

Masked Input

Text field that enforces a character mask as you type: dates, card numbers, phone numbers, license keys. The mask’s literals are typed for the user; value holds only the raw characters, and the raw value is what forms receive, so the mask stays presentation.

ComponentsMasked Input
inputinteractive
<arc-masked-input>

Overview

MaskedInput is a single-line text field that formats fixed-shape values while the user types them. The `mask` prop describes the shape with four slot characters (`#` for a digit, `A` for an uppercase letter (lowercase input is uppercased), `a` for any letter, and `*` for a letter or digit). Every other character is a literal that the component types for the user. A date mask of `##/##/####` means the user types eight digits and the slashes appear on their own, with the caret always landing on the next position that can accept a character. `value` holds only the raw characters, never the mask's literals. A completed date field reports `12042026`, and that raw string is also what the field submits with a form. The formatted string, `12/04/2026`, is presentation, available read-only as `formattedValue` and in the `formatted` key of every event detail. This means switching a card mask from space-grouped to dash-grouped changes nothing downstream, and your backend never has to strip formatting it did not ask for. Editing behaves the way users expect from a good mask. Typing inserts at the caret and skips literals forward; Backspace deletes the previous fillable character, skipping literals backward; pasting strips non-conforming characters and fills the remaining positions, so a card number pasted with dashes lands cleanly in a space-grouped mask. A character that does not fit its slot is silently rejected and the caret stays put. Before typing begins, the native placeholder shows the mask shape; once typing starts, the unfilled remainder renders in the field as a muted hint, such as `12/__/____`. MaskedInput follows the v3 commit contract: `arc-input` fires on each accepted edit, and `arc-change` fires on blur or Enter when the value changed, and immediately when the last mask position fills, the same fixed-length commit Pin Input uses. Constraint validation is built in: a required empty field fails with `valueMissing`, and a partially filled mask fails with an "Incomplete value" pattern error, so a half-typed card number cannot pass a form's validation.

Guidelines

When to use

  • Use a mask when the value has one fixed, well-known shape: dates, card numbers, phone numbers in a single locale, license or serial keys
  • Read `value` (or the form submission) for storage and `formattedValue` only for display. The raw value is the contract
  • Listen for `arc-change` to validate or submit. It fires the moment the mask completes, so users need not leave the field first
  • Set `autocomplete` to the matching token (for example `cc-number` on a card field) so browser autofill keeps working
  • Always provide a `label`; the mask shape in the placeholder is a hint, not a name for the field
  • Prefer `A` over `a` for license and product keys so the stored value is case-normalized without the user caring

When not to use

  • Do not mask free-form values like names, email addresses, or search queries. A mask that fights variable-length input is worse than no mask; use Input instead
  • Do not use MaskedInput for short fixed-length verification codes. Pin Input gives each character its own box and auto-advances
  • Do not mask international phone numbers with a single pattern. Number lengths vary by country, and a wrong mask locks users out of entering their own number
  • Do not parse `formattedValue` on the server. Submit and store the raw value, and format at the display edge
  • Do not use the mask as a substitute for validation of meaning. `##/##/####` accepts 99/99/9999; check that a date is real before accepting it

Features

  • Declarative mask pattern: `#` digit, `A` uppercase letter, `a` any letter, `*` alphanumeric, everything else a literal
  • Raw `value` with no literals. The formatted string is exposed separately as read-only `formattedValue`
  • Forms receive the raw value, so the presentation format never leaks into submitted data
  • Literal skipping in both directions: typing jumps forward past literals, Backspace deletes through them
  • Paste support that strips non-conforming characters and fills the remaining positions
  • Muted in-field hint for unfilled positions once typing starts, configurable via `placeholder-char`
  • Fires `arc-input` per accepted edit and `arc-change` on blur, Enter, or the moment the mask completes
  • Built-in validation: required-empty is `valueMissing`, a partial fill is an "Incomplete value" pattern error
  • Numeric masks automatically request the numeric keyboard on mobile
  • Prefix and suffix slots, label, sizes, and disabled/readonly states matching Input

Preview

Usage

This component requires JavaScript. No pure HTML/CSS version is available. Use the Web Component directly or a framework wrapper.

<!-- Date, card, and license-key masks -->
<div style="display:flex; flex-direction:column; width:100%; max-width:400px; gap:16px;">
  <arc-masked-input label="Expiry date" name="expiry" mask="##/##/####" autocomplete="cc-exp"></arc-masked-input>
  <arc-masked-input label="Card number" name="card" mask="#### #### #### ####" autocomplete="cc-number"></arc-masked-input>
  <arc-masked-input label="License key" name="license" mask="AAA-###-AAA" placeholder-char="•"></arc-masked-input>
</div>

<script>
  const card = document.querySelector('[name="card"]');
  card.addEventListener('arc-change', (e) => {
    // e.detail.value is the raw digits; e.detail.formatted is presentation.
    console.log(e.detail.value, e.detail.formatted);
  });
</script>

API

autoValidatesbooleanfalse
Runs its own constraint logic and owns the whole validity flag set.
formattedValue
The formatted presentation string: raw characters interleaved with mask literals, e.g. raw 12042026 under a date mask reads 12/04/2026. Read-only: it is derived from value and mask, never stored, and never submitted.
maskstring''
The mask pattern. # accepts a digit, A an uppercase letter (lowercase input is uppercased), a any letter, * a letter or digit; every other character is a literal typed for the user. Examples: ##/##/####, #### #### #### ####, AAA-###.
valuestring''
The RAW accepted characters only, with no mask literals: 12042026, never 12/04/2026. The formatted string is presentation; read it from formattedValue. Programmatic values are conformed against the mask, so setting a formatted string keeps only the characters the mask accepts.
placeholder-charstring'_'
Character rendered in unfilled positions of the in-field hint once typing starts (for example 12/__/____). Before any input, the native placeholder shows the full mask shape. Defaults to _.
labelstring''
Visible label rendered above the field. Automatically associated with the field via a generated id, ensuring screen readers announce it correctly.
namestring''
The name attribute sent with form data on submission. The submitted value is the RAW value, without mask literals.
disabledbooleanfalse
Prevents user interaction and applies a muted visual treatment. The field value is excluded from form submission when disabled.
autocompletestring''
Passed through to the inner input, e.g. cc-number on a card field so browser autofill can offer saved cards.
errorstring''
Error message displayed below the input. When set, the input border turns red and the error text appears.
requiredbooleanfalse
Marks the field as required. An empty field fails validation with valueMissing; a partially filled one fails with an "Incomplete value" pattern error.
readonlybooleanfalse
Prevents the user from editing the value while keeping the field focusable, and the value is still submitted with the form.
size'sm' | 'md' | 'lg''md'
Controls the input size. Options: 'sm', 'md', 'lg'.
formAssociatedbooleantrue
propertiesobject{ // flag(), unlike `disabled`. The exclusion in props.js is specifically // about form-associated *platform* semantics: a `disabled` content // attribute that is merely present makes the element actually disabled // per the HTML spec, and formDisabledCallback assigns the property back, // so no converter can win. Neither of these is platform-mapped: // `required` is enforced by _computeValidity() below and `readonly` by // each component's own interaction handlers, so the stock converter buys // nothing here and costs the usual bug: `required="false"` read as true, // blocking submission of a form the author meant to leave optional. // Finding #48's shape, across all 26 form controls at once. required: flag(false), readonly: flag(false), }
Lit merges static properties up the prototype chain, so every consumer gets these without declaring them. required participates in constraint validation below; readonly reflects for styling and is enforced by each component's interaction handlers (the mixin can't know which gestures mutate state).
form
validity
validationMessage

Methods

checkValidity()boolean
Whether the control currently satisfies its constraints, per the native constraint-validation API. Fires invalid on the element when it does not, and reports nothing to the user.
reportValidity()boolean
As checkValidity(), but also shows the browser's validation message against the control when it fails.

Events

arc-inputdetail:{ value: string, formatted: string }
Fired on each accepted edit. value is the raw characters; formatted is the presentation string. A rejected character fires nothing.
arc-changedetail:{ value: string, formatted: string }
Fired on blur or Enter when the value changed, and immediately when the last mask position fills. A complete mask is a committed value (the fixed-length precedent set by OTP Input).

See Also