Phone Input Default
Phone Input Default is a React and Tailwind CSS form component from UI Layouts, licensed MIT. Copy it and paste it into your project.
UI Layouts
MIT
forms
What it is
Phone Input Default is a React and Tailwind CSS form component from UI Layouts, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
Needs
npm i lucide-react react-phone-number-input zod
Licence
This component comes from UI Layouts and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Phone Input Default
'use client';
import { Button } from '@/components/website/ui/button';
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/components/website/ui/command';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/website/ui/popover';
import { ScrollArea } from '@/components/website/ui/scroll-area';
import { cn } from '@/lib/utils';
import { CheckIcon, ChevronsUpDown } from 'lucide-react';
import * as React from 'react';
import * as RPNInput from 'react-phone-number-input';
import flags from 'react-phone-number-input/flags';
type PhoneInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'value'> &
Omit<RPNInput.Props<typeof RPNInput.default>, 'onChange'> & {
onChange?: (value: RPNInput.Value) => void;
};
const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps> = React.forwardRef<
React.ElementRef<typeof RPNInput.default>,
PhoneInputProps
>(({ className, onChange, ...props }, ref) => {
return (
<RPNInput.default
ref={ref}
className={cn('flex', className)}
flagComponent={FlagComponent}
countrySelectComponent={CountrySelect}
inputComponent={InputComponent}
/**
* Handles the onChange event.
*
* react-phone-number-input might trigger the onChange event as undefined
* when a valid phone number is not entered. To prevent this,
* the value is coerced to an empty string.
*
* @param {E164Number | undefined} value - The entered value
*/
// @ts-expect-error
onChange={(value) => onChange?.(value || '')}
{...props}
/>
);
});
PhoneInput.displayName = 'PhoneInput';
const InputComponent = React.forwardRef<HTMLInputElement, any>(({ className, ...props }, ref) => (
<input
className={cn(
'rounded-e-lg rounded-s-none px-2 bg-primary-base outline-hidden w-full ',
className
)}
{...props}
ref={ref}
/>
));
InputComponent.displayName = 'InputComponent';
type CountrySelectOption = { label: string; value: RPNInput.Country };
type CountrySelectProps = {
disabled?: boolean;
value: RPNInput.Country;
onChange: (value: RPNInput.Country) => void;
options: CountrySelectOption[];
};
const CountrySelect = ({ disabled, value, onChange, options }: CountrySelectProps) => {
const handleSelect = React.useCallback(
(country: RPNInput.Country) => {
onChange(country);
},
[onChange]
);
return (
<Popover>
<PopoverTrigger asChild>
<Button
type='button'
variant={'outline'}
className={cn('flex gap-1 rounded-e-none bg-primary-base rounded-s-lg px-3')}
disabled={disabled}
>
<FlagComponent country={value} countryName={value} />
<ChevronsUpDown
className={cn('-mr-2 h-4 w-4 opacity-50', disabled ? 'hidden' : 'opacity-100')}
/>
</Button>
</PopoverTrigger>
<PopoverContent className='w-[300px] p-0'>
<Command className='dark:bg-neutral-950 border dark:border-neutral-800 border-neutral-200'>
<CommandList>
<ScrollArea className='h-72'>
<CommandInput placeholder='Search country...' />
<CommandEmpty>No country found.</CommandEmpty>
<CommandGroup>
{options
.filter((x) => x.value)
.map((option) => (
<CommandItem
className='gap-2'
key={option.value}
onSelect={() => handleSelect(option.value)}
>
<FlagComponent country={option.value} countryName={option.label} />
<span className='flex-1 text-sm'>{option.label}</span>
{option.value && (
<span className='text-foreground/50 text-sm'>
{`+${RPNInput.getCountryCallingCode(option.value)}`}
</span>
)}
<CheckIcon
className={cn(
'ml-auto h-4 w-4',
option.value === value ? 'opacity-100' : 'opacity-0'
)}
/>
</CommandItem>
))}
</CommandGroup>
</ScrollArea>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
};
const FlagComponent = ({ country, countryName }: RPNInput.FlagProps) => {
const Flag = flags[country];
return (
<span className='bg-foreground/20 flex h-4 w-6 overflow-hidden rounded-xs'>
{Flag && <Flag title={countryName} />}
</span>
);
};
FlagComponent.displayName = 'FlagComponent';
export { PhoneInput };
// @ts-nocheck
'use client';
import { PhoneInput } from '@/components/ui/phone-input';
import { zodResolver } from '@hookform/resolvers/zod';
import { Controller, useForm } from 'react-hook-form';
import { isValidPhoneNumber } from 'react-phone-number-input';
import { z } from 'zod';
// Zod schema for phone validation
const FormSchema = z.object({
phone: z
.string()
.min(1, 'Phone number is required')
.refine((value) => value && isValidPhoneNumber(value), {
message: 'Invalid phone number',
}),
});
type FormData = z.infer<typeof FormSchema>;
export default function PhoneNumber() {
const {
control,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(FormSchema),
defaultValues: { phone: '' },
});
const onSubmit = (data: FormData) => {
alert(`You submitted the following values: ${JSON.stringify(data)}`);
};
return (
<div className='relative mt-2 flex justify-center'>
<form onSubmit={handleSubmit(onSubmit)} className='flex flex-col items-start space-y-3'>
{/* Phone Input Field */}
<div className='flex flex-col items-start'>
<label htmlFor='phone' className='text-left'>
Phone Number
</label>
<Controller
name='phone'
control={control}
render={({ field }) => (
<PhoneInput
{...field}
id='phone'
placeholder='Enter a phone number'
className='w-full border rounded-lg'
/>
)}
/>
{errors.phone && <p className='text-red-500 text-sm mt-1'>{errors.phone.message}</p>}
</div>
{/* Submit Button */}
<button type='submit' className='p-2 bg-primary text-background rounded-md text-sm'>
Submit
</button>
</form>
</div>
);
}
import { Slot } from '@radix-ui/react-slot';
import { type VariantProps, cva } from 'class-variance-authority';
import * as React from 'react';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
'border border-input dark:bg-neutral-800 bg-neutral-50 hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
uilayouts:
'dark:bg-zinc-900 bg-neutral-200 dark:text-white text-black border dark:border-neutral-800',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
);
}
);
Button.displayName = 'Button';
export { Button, buttonVariants };
'use client';
import { Dialog, DialogContent } from '@/components/website/ui/dialog';
import { cn } from '@/lib/utils';
import type { DialogProps } from '@radix-ui/react-dialog';
import { Command as CommandPrimitive } from 'cmdk';
import { Search } from 'lucide-react';
import * as React from 'react';
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
>(({ className, ...props }, ref) => (
<CommandPrimitive
ref={ref}
className={cn(
'flex h-full w-full flex-col overflow-hidden rounded-md dark:bg-neutral-800 bg-neutral-50 text-popover-foreground',
className
)}
{...props}
/>
));
Command.displayName = CommandPrimitive.displayName;
interface CommandDialogProps extends DialogProps {}
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
return (
<Dialog {...props}>
<DialogContent className='overflow-hidden p-0'>
<Command className='[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5'>
{children}
</Command>
</DialogContent>
</Dialog>
);
};
const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
<div className='flex items-center border-b px-3' cmdk-input-wrapper=''>
<Search className='mr-2 h-4 w-4 shrink-0 opacity-50' />
<CommandPrimitive.Input
ref={ref}
className={cn(
'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
className
)}
{...props}
/>
</div>
));
CommandInput.displayName = CommandPrimitive.Input.displayName;
const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => (
<CommandPrimitive.List
ref={ref}
className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}
{...props}
/>
));
CommandList.displayName = CommandPrimitive.List.displayName;
const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => (
<CommandPrimitive.Empty ref={ref} className='py-6 text-center text-sm' {...props} />
));
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Group
ref={ref}
className={cn(
'overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground',
className
)}
{...props}
/>
));
CommandGroup.displayName = CommandPrimitive.Group.displayName;
const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Separator
ref={ref}
className={cn('-mx-1 h-px bg-border', className)}
{...props}
/>
));
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Item
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center rounded-xs px-2 py-1.5 text-sm outline-hidden dark:aria-selected:bg-neutral-950 aria-selected:bg-neutral-200 aria-selected:text-accent-foreground data-[disabled="true"]:pointer-events-none data-[disabled="true"]:opacity-50',
className
)}
{...props}
/>
));
CommandItem.displayName = CommandPrimitive.Item.displayName;
const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}
{...props}
/>
);
};
CommandShortcut.displayName = 'CommandShortcut';
export {
Command,
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
};
'use client';
import * as PopoverPrimitive from '@radix-ui/react-popover';
import * as React from 'react';
import { cn } from '@/lib/utils';
const Popover = PopoverPrimitive.Root;
const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverContent, PopoverTrigger };
'use client';
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
import * as React from 'react';
import { cn } from '@/lib/utils';
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={cn('relative overflow-hidden', className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className='h-full w-full rounded-[inherit]'>
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
));
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
const ScrollBar = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = 'vertical', ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
'flex touch-none select-none transition-colors',
orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-px',
orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-px',
className
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className='relative flex-1 rounded-full dark:bg-neutral-800 bg-neutral-200' />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
));
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
export { ScrollArea, ScrollBar };