shadcn/ui
shadcn/ui provides styled, accessible components built on Radix UI. Since raqam is headless, it integrates cleanly — just use raqam’s hooks/components and apply shadcn’s class names.
RaqamInput component
Section titled “RaqamInput component”Create a reusable RaqamInput component that follows the shadcn design language:
"use client";
import * as React from "react";import { NumberField } from "raqam";import { cn } from "@/lib/utils";import type { UseNumberFieldStateOptions } from "raqam";
interface RaqamInputProps extends UseNumberFieldStateOptions { label?: string; description?: string; className?: string;}
export function RaqamInput({ label, description, className, ...props}: RaqamInputProps) { return ( <NumberField.Root {...props} className={cn("flex flex-col gap-2", className)} > {label && ( <NumberField.Label className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"> {label} </NumberField.Label> )}
<NumberField.Group className="flex h-9 rounded-md border border-input bg-transparent shadow-sm overflow-hidden group-data-[focused]:ring-1 group-data-[focused]:ring-ring group-data-[invalid]:border-destructive"> <NumberField.Decrement className="px-3 text-muted-foreground hover:text-foreground disabled:opacity-50 border-r border-input bg-muted/30 hover:bg-muted/50 transition-colors select-none text-sm"> − </NumberField.Decrement>
<NumberField.Input className="flex-1 px-3 py-1 text-sm outline-none bg-transparent placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50" />
<NumberField.Increment className="px-3 text-muted-foreground hover:text-foreground disabled:opacity-50 border-l border-input bg-muted/30 hover:bg-muted/50 transition-colors select-none text-sm"> + </NumberField.Increment> </NumberField.Group>
{description && ( <NumberField.Description className="text-xs text-muted-foreground"> {description} </NumberField.Description> )}
<NumberField.ErrorMessage className="text-xs text-destructive" /> </NumberField.Root> );}import { RaqamInput } from "@/components/raqam-input";
// Basic<RaqamInput locale="en-US" label="Quantity" defaultValue={1} minValue={1} step={1}/>
// Currency<RaqamInput locale="en-US" label="Price" description="Enter the listing price in USD" formatOptions={{ style: "currency", currency: "USD" }} defaultValue={0} minValue={0} validate={(v) => v !== null && v > 0 ? true : "Price must be greater than $0"}/>Display-only with shadcn Badge
Section titled “Display-only with shadcn Badge”import { Badge } from "@/components/ui/badge";import { useNumberFieldFormat } from "raqam";
function PriceBadge({ amount }: { amount: number }) { const formatted = useNumberFieldFormat(amount, { locale: "en-US", formatOptions: { style: "currency", currency: "USD" }, });
return ( <Badge variant="secondary" className="font-mono"> {formatted} </Badge> );}In a shadcn Form (with react-hook-form)
Section titled “In a shadcn Form (with react-hook-form)”import { useForm } from "react-hook-form";import { Form, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";import { NumberField } from "raqam";import { Controller } from "react-hook-form";
export function ProductForm() { const form = useForm({ defaultValues: { price: null as number | null } });
return ( <Form {...form}> <form onSubmit={form.handleSubmit(console.log)}> <FormField control={form.control} name="price" rules={{ required: "Price is required" }} render={({ field }) => ( <FormItem> <FormLabel>Price</FormLabel> <NumberField.Root locale="en-US" formatOptions={{ style: "currency", currency: "USD" }} value={field.value} onChange={field.onChange} onBlur={field.onBlur} className="w-full" > <NumberField.Group className="flex h-9 rounded-md border border-input overflow-hidden focus-within:ring-1 focus-within:ring-ring"> <NumberField.Decrement className="px-3 bg-muted/30 border-r border-input text-sm text-muted-foreground hover:bg-muted/50"> − </NumberField.Decrement> <NumberField.Input className="flex-1 px-3 text-sm outline-none bg-transparent" /> <NumberField.Increment className="px-3 bg-muted/30 border-l border-input text-sm text-muted-foreground hover:bg-muted/50"> + </NumberField.Increment> </NumberField.Group> </NumberField.Root> <FormMessage /> </FormItem> )} /> </form> </Form> );}