Provides flexible masked input fields for enforcing specific input formats like phone numbers, dates, or IDs.
react-input-mask1'use client'
2
3import React from 'react'
4
5import { InputMaskField } from '@/components/ui/input-mask'
6import { Label } from '@/components/ui/label'
7
8export function InputMaskDemo() {
9 const [serial, setSerial] = React.useState('')
10
11 return (
12 <div className="space-y-4">
13 <div className="space-y-2">
14 <Label>SSN</Label>
15 <InputMaskField
16 id="ssn"
17 mask="999-99-9999"
18 placeholder="999-99-9999"
19 />
20 </div>
21
22 <div className="space-y-2">
23 <Label>Phone</Label>
24 <InputMaskField
25 id="phone"
26 mask="(999) 999-9999"
27 placeholder="(999) 999-9999"
28 />
29 </div>
30
31 <div className="space-y-2">
32 <Label>Serial</Label>
33 <InputMaskField
34 id="serial"
35 value={serial}
36 onChange={(e) => setSerial(e.target.value)}
37 mask="a*-999-a999"
38 placeholder="a*-999-a999"
39 />
40 </div>
41 </div>
42 )
43}
44Install the following dependencies:
1'use client'
2
3import React from 'react'
4
5import { InputMaskField } from '@/components/ui/input-mask'
6import { Label } from '@/components/ui/label'
7
8export function InputMaskCustomExamples() {
9 return (
10 <div className="space-y-6">
11 <div className="space-y-2">
12 <Label>License Number</Label>
13 <InputMaskField
14 id="license-number"
15 mask="AAA-9999"
16 placeholder="ABC-1234"
17 formatChars={{
18 A: '[A-Z]',
19 9: '[0-9]',
20 }}
21 />
22 </div>
23
24 <div className="space-y-2">
25 <Label>Product Code</Label>
26 <InputMaskField
27 id="product-code"
28 mask="AA-9999-****"
29 placeholder="PR-1234-****"
30 formatChars={{
31 A: '[A-Z]',
32 9: '[0-9]',
33 '*': '[A-Za-z0-9]',
34 }}
35 />
36 </div>
37
38 <div className="space-y-2">
39 <Label>Invoice Number</Label>
40 <InputMaskField
41 id="invoice-number"
42 mask="INV-999999"
43 placeholder="INV-123456"
44 formatChars={{
45 9: '[0-9]',
46 }}
47 />
48 </div>
49
50 <div className="space-y-2">
51 <Label>Vehicle Registration</Label>
52 <InputMaskField
53 id="vehicle-number"
54 mask="AA-99-AA-9999"
55 placeholder="MH-12-AB-1234"
56 formatChars={{
57 A: '[A-Z]',
58 9: '[0-9]',
59 }}
60 />
61 </div>
62 </div>
63 )
64}
651'use client'
2
3import React from 'react'
4
5import { InputMaskField } from '@/components/ui/input-mask'
6import { Label } from '@/components/ui/label'
7
8export function InputMaskPermanentsExamples() {
9 const [date, setDate] = React.useState('')
10 const [phone, setPhone] = React.useState('')
11 const [time, setTime] = React.useState('')
12
13 return (
14 <div className="space-y-6">
15 <div className="space-y-2">
16 <Label>Date (MM/DD/YYYY)</Label>
17 <InputMaskField
18 id="date"
19 value={date}
20 onChange={(e) => setDate(e.target.value)}
21 mask="99/99/9999"
22 placeholder="MM/DD/YYYY"
23 permanents={[2, 5]}
24 formatChars={{ '9': '[0-9]' }}
25 />
26 </div>
27
28 <div className="space-y-2">
29 <Label>Time (HH:MM)</Label>
30 <InputMaskField
31 id="time"
32 value={time}
33 onChange={(e) => setTime(e.target.value)}
34 mask="99:99"
35 placeholder="12:00"
36 permanents={[2]}
37 formatChars={{ '9': '[0-9]' }}
38 />
39 </div>
40 </div>
41 )
42}
431'use client'
2
3import React from 'react'
4import { Controller, useForm } from 'react-hook-form'
5import { toast } from 'sonner'
6
7import { Button } from '@/components/ui/button'
8import {
9 Card,
10 CardContent,
11 CardHeader,
12 CardTitle,
13} from '@/components/ui/card'
14import { Input } from '@/components/ui/input'
15import { InputMaskField } from '@/components/ui/input-mask'
16import { Label } from '@/components/ui/label'
17
18type FormData = {
19 name: string
20 email: string
21 phone: string
22 dob: string
23 ip: string
24 creditCard: string
25 panCard: string
26 ssn: string
27}
28
29export function MaskedFormRHFDemo() {
30 const { register, handleSubmit, control, reset, formState } =
31 useForm<FormData>({
32 defaultValues: {
33 name: '',
34 email: '',
35 phone: '',
36 dob: '',
37 ip: '',
38 creditCard: '',
39 panCard: '',
40 ssn: '',
41 },
42 })
43
44 const onSubmit = () => {
45 toast.success('Form submitted successfully')
46 reset()
47 }
48
49 // Helper to check incomplete value (mask)
50 const isIncomplete = (value?: string) =>
51 typeof value === 'string' && value.includes('_')
52
53 return (
54 <Card>
55 <CardHeader>
56 <CardTitle>Masked Form</CardTitle>
57 </CardHeader>
58 <CardContent>
59 <form
60 onSubmit={handleSubmit(onSubmit)}
61 className="max-w-lg space-y-2"
62 >
63 <div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
64 <div className="space-y-1">
65 <Label>Name</Label>
66 <Input
67 {...register('name', { required: true })}
68 placeholder="Rafael Costa"
69 />
70 </div>
71
72 <div className="space-y-1">
73 <Label>Email</Label>
74 <Input
75 {...register('email', { required: true })}
76 type="email"
77 placeholder="rafael.costa@example.com"
78 />
79 </div>
80 </div>
81
82 <div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
83 <div className="space-y-1">
84 <Label>Phone</Label>
85 <Controller
86 name="phone"
87 control={control}
88 render={({ field }) => (
89 <InputMaskField
90 mask="(999) 999-9999"
91 placeholder="(123) 456-7890"
92 {...field}
93 onBlur={(e) => {
94 if (isIncomplete(e.target.value))
95 field.onChange('')
96 }}
97 />
98 )}
99 />
100 </div>
101
102 <div className="space-y-1">
103 <Label>Date of Birth</Label>
104 <Controller
105 name="dob"
106 control={control}
107 render={({ field }) => (
108 <InputMaskField
109 mask="99/99/9999"
110 placeholder="MM/DD/YYYY"
111 permanents={[2, 5]}
112 formatChars={{ '9': '[0-9]' }}
113 {...field}
114 onBlur={(e) => {
115 if (isIncomplete(e.target.value))
116 field.onChange('')
117 }}
118 />
119 )}
120 />
121 </div>
122 </div>
123
124 <div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
125 <div className="space-y-1">
126 <Label>Credit Card</Label>
127 <Controller
128 name="creditCard"
129 control={control}
130 render={({ field }) => (
131 <InputMaskField
132 mask="9999 9999 9999 9999"
133 placeholder="1234 5678 9012 3456"
134 {...field}
135 onBlur={(e) => {
136 if (isIncomplete(e.target.value))
137 field.onChange('')
138 }}
139 />
140 )}
141 />
142 </div>
143 <div className="space-y-1">
144 <Label>Pan Card</Label>
145 <Controller
146 name="panCard"
147 control={control}
148 render={({ field }) => (
149 <InputMaskField
150 mask="AaAa9999999A"
151 placeholder="AbCd9999999E"
152 {...field}
153 onBlur={(e) => {
154 if (isIncomplete(e.target.value))
155 field.onChange('')
156 }}
157 formatChars={{
158 A: '[A-Z]',
159 a: '[a-z]',
160 '9': '[0-9]',
161 }}
162 />
163 )}
164 />
165 </div>
166 </div>
167
168 <div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
169 <div className="space-y-1">
170 <Label>IP Address</Label>
171 <Controller
172 name="ip"
173 control={control}
174 render={({ field }) => (
175 <InputMaskField
176 mask="999.999.999.999"
177 placeholder="192.168.001.001"
178 formatChars={{ '9': '[0-9]' }}
179 {...field}
180 onBlur={(e) => {
181 if (isIncomplete(e.target.value))
182 field.onChange('')
183 }}
184 />
185 )}
186 />
187 </div>
188 <div className="space-y-1">
189 <Label>SSN</Label>
190 <Controller
191 name="ssn"
192 control={control}
193 render={({ field }) => (
194 <InputMaskField
195 mask="999-99-9999"
196 placeholder="123-45-6789"
197 {...field}
198 onBlur={(e) => {
199 if (isIncomplete(e.target.value))
200 field.onChange('')
201 }}
202 />
203 )}
204 />
205 </div>
206 </div>
207
208 <div className="flex justify-end gap-2">
209 <Button type="submit">Submit</Button>
210 <Button
211 type="button"
212 variant="outline"
213 onClick={() => reset()}
214 >
215 Reset
216 </Button>
217 </div>
218 </form>
219 </CardContent>
220 </Card>
221 )
222}
223| Prop | Type | Default |
|---|---|---|
| mask | string | - |
| formatChars | { [key: string]: string } | - |
| permanents | number[] | - |
| value | string | - |
| onChange | (e: React.ChangeEvent<HTMLInputElement>) => void | - |