Next.js Input Mask
A Next.js input mask for the App Router in AdminKit PRO: a client component that loads Inputmask in the browser and masks dates, amounts and IP addresses.
This component is only available in AdminKit PRO. Learn more.
Some fields only make sense in one format: a date, an amount, an IP address. MaskedInput keeps people to that format while they type, with Inputmask 5 on a plain <input type="text">. It is part of the PRO version of AdminKit for Next.js.
MaskedInput is a client component, a file that starts with "use client", because Inputmask works in the browser. The HTML that Next.js renders on the server has the plain, unmasked input. Once the browser has taken over that HTML (hydration), a useEffect imports Inputmask with a dynamic import() and applies the mask. So the library never runs on the server, and it only loads on pages that use it.
Example
Your page can stay a server component and render MaskedInput directly, as long as mask is a string or an object of plain values. The demo's Advanced Inputs page does this:
import { MaskedInput } from "@/components/plugins/masked-input";
import { pageMetadata } from "@/lib/site";
export const metadata = pageMetadata("/vehicles");
export default function VehiclesPage() {
return (
<>
<h1 className="h3 mb-3">Vehicles</h1>
<div className="mb-3">
<label className="form-label" htmlFor="maskDate">Date</label>
<MaskedInput
className="form-control"
id="maskDate"
mask={{ alias: "datetime", inputFormat: "dd/mm/yyyy" }}
aria-describedby="maskDateHelp"
/>
<div className="form-text" id="maskDateHelp">e.g "DD/MM/YYYY"</div>
</div>
<div>
<label className="form-label" htmlFor="maskPlate">License plate</label>
<MaskedInput
className="form-control"
id="maskPlate"
mask="[9-]AAA-999"
aria-describedby="maskPlateHelp"
/>
<div className="form-text" id="maskPlateHelp">e.g "9-ABC-123"</div>
</div>
</>
);
}9 is a digit, A a letter (shown in upper case), and [ ] marks an optional part.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
mask | string | MaskOptions | A mask string, or Inputmask's options (mask, alias, regex and others); read once, after the input mounts | |
ref | Ref<HTMLInputElement> | The input element, merged with the component's own ref |
Other props, such as className, id, name, placeholder and defaultValue, go to the <input>. type defaults to "text". A server component can only pass plain data to the browser, so Inputmask options that are functions, such as onBeforeMask, cannot come from it. Pass them from a client component. The component itself is in src/components/plugins/masked-input.tsx.
Aliases
For common formats, Inputmask has aliases: named masks with their own options. These are the masks of the Advanced Inputs demo:
| Field | mask | Example |
|---|---|---|
| Date | { alias: "datetime", inputFormat: "dd/mm/yyyy" } | 31/12/2026 |
| Date | { alias: "datetime", inputFormat: "mm/dd/yyyy" } | 12/31/2026 |
| Currency | { alias: "numeric", groupSeparator: ",", digits: 2, digitsOptional: false, prefix: "€ ", placeholder: "0" } | € 9.95 |
| Decimal | { alias: "decimal", groupSeparator: "," } | 123,456,789 |
| IP address | { alias: "ip" } | 123.123.123.123 |
{ alias: "email" } | support@adminkit.io |
<label className="form-label" htmlFor="maskCurrency">Amount</label>
<MaskedInput
className="form-control"
id="maskCurrency"
mask={{
alias: "numeric",
groupSeparator: ",",
digits: 2,
digitsOptional: false,
prefix: "$ ",
placeholder: "0",
}}
/>See the Inputmask README for all options.
Reading the value
Inputmask writes the input's value itself, so MaskedInput is uncontrolled: never pass value. Read the value from the form when it is submitted, in a client component. Then post it to your API with fetch, because a static export has no Server Actions:
"use client";
import type { FormEvent } from "react";
import { MaskedInput } from "@/components/plugins/masked-input";
export function PhoneForm() {
const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
await fetch("https://api.example.com/profile", {
method: "POST",
body: new FormData(event.currentTarget),
});
};
return (
<form onSubmit={onSubmit}>
<label className="form-label" htmlFor="phone">Phone</label>
<MaskedInput className="form-control mb-3" id="phone" name="phone" mask="(999) 999-9999" />
<button type="submit" className="btn btn-primary">Save</button>
</form>
);
}The submitted value keeps the mask's literal characters, such as (555) 123-4567. A ref gives you the input element too. mask is read once. To change it, render a new input with another key.
Accessibility
- Label the input with
<label htmlFor>and itsid. - Screen readers do not announce the mask. Show the format in help text linked with
aria-describedby. - Add
inputMode("numeric","decimal","email") for the right mobile keyboard.
Related
- React input mask: the same component in AdminKit for React (Vite)
- Bootstrap 5 input mask: the input mask of the HTML version
- Form controls: plain inputs and submitting a form
- Advanced Inputs demo
Last updated on
Checkbox and radio
Next.js Bootstrap checkbox, radio and switch markup: plain checks in server components, and toggle buttons that keep their state in a client component.
Date picker
A Next.js date picker for the App Router with Vanilla Calendar Pro: a client component for an inline calendar, and in AdminKit PRO a date and time input.