Date of Birth Input Component¶
Overview¶
The DOBFieldInput component provides a user-friendly date of birth input using three separate text fields (day, month, year) instead of the native HTML5 date picker. This approach offers:
- Consistent behavior across browsers and devices
- Better mobile usability (no awkward date picker wheels)
- Locale-aware field ordering (MDY vs DMY)
- Clear validation feedback
Architecture¶
Files¶
| File | Purpose |
|---|---|
components/ui/DOBFieldInput.tsx |
Main reusable component |
lib/hooks/useDateFormat.ts |
Domain-based format detection |
lib/utils/dateValidation.ts |
Validation and formatting utilities |
Component Props¶
interface DOBFieldInputProps {
value: string; // YYYY-MM-DD format (backend compatible)
onChange: (value: string) => void;
disabled?: boolean;
error?: string; // External error message to display
showHint?: boolean; // Show format hint (e.g., "MM/DD/YYYY")
}
Date Format Detection¶
The useDateFormat hook determines field order based on the current domain:
| Domain | Format | Field Order |
|---|---|---|
localhost |
MDY | Month → Day → Year |
oterahealth.* |
MDY | Month → Day → Year |
oktahealth.* |
DMY | Day → Month → Year |
| Other domains | DMY | Day → Month → Year |
import { useDateFormat } from "@/lib/hooks/useDateFormat";
const { format, order, placeholder } = useDateFormat();
// format: "MDY" or "DMY"
// order: ["month", "day", "year"] or ["day", "month", "year"]
// placeholder: "MM/DD/YYYY" or "DD/MM/YYYY"
Features¶
Auto-Advance¶
When a field reaches its maximum length (2 digits for day/month, 4 for year), focus automatically advances to the next field.
Backspace Navigation¶
Pressing backspace on an empty field moves focus to the previous field.
Validation on Blur¶
When leaving a field, the component: 1. Auto-corrects invalid values (e.g., month "15" → "12") 2. Pads single digits with leading zeros (e.g., "5" → "05") 3. Validates the complete date if all fields are filled 4. Shows appropriate error messages
Date Preview¶
When all three fields contain valid values, a human-readable date string (e.g., "March 5, 1990") appears below the input fields. This lets users quickly confirm the date they entered is correct, which is especially helpful when the field order differs between locales. The preview updates live as fields change and disappears if any field is empty or invalid.
Validation Rules¶
- Day: 1-31 (refined by month and year)
- Month: 1-12
- Year: 1900 to current year
- Future dates: Not allowed
- Leap years: Feb 29 only valid in leap years
- Invalid dates: Feb 30, Apr 31, etc. are rejected
Usage Examples¶
Basic Usage¶
import DOBFieldInput from "@/components/ui/DOBFieldInput";
const [dateOfBirth, setDateOfBirth] = useState("");
<DOBFieldInput
value={dateOfBirth}
onChange={setDateOfBirth}
/>
With External Error¶
Disabled State¶
Without Format Hint¶
Data Flow¶
- User types in individual fields
- Values are tracked in refs (synchronous) and state (for display)
- On blur, values are validated and formatted
- Parent receives
YYYY-MM-DDformat string viaonChange - Backend APIs expect and return
YYYY-MM-DDformat
Validation Utilities¶
validateDateOfBirth(day, month, year)¶
Returns validation result with error details:
interface DateValidationResult {
isValid: boolean;
error?: string; // Translation key
errorField?: "day" | "month" | "year";
}
formatDateForBackend(day, month, year)¶
Converts individual fields to YYYY-MM-DD format:
parseDateFromBackend(dateString)¶
Converts YYYY-MM-DD to individual fields:
Translations¶
The component uses the following translation keys:
{
"dob": {
"day": "DD",
"month": "MM",
"year": "YYYY",
"dayLabel": "Day",
"monthLabel": "Month",
"yearLabel": "Year",
"errors": {
"invalidDay": "Invalid day",
"invalidMonth": "Invalid month",
"invalidYear": "Invalid year",
"futureDate": "Date cannot be in the future"
}
}
}
Where It's Used¶
| Component | Context |
|---|---|
DOBInput.tsx |
Patient onboarding - DOB verification step |
InviteNewPatientModal.tsx |
Therapist inviting new patient |
ResendInviteModal.tsx |
Collecting DOB when resending invite |
PatientManagementModal.tsx |
Editing patient details |
Technical Notes¶
Race Condition Handling¶
The component uses refs to track the latest typed values synchronously, avoiding issues with React's batched state updates when auto-advance triggers blur events.
SSR Compatibility¶
The useDateFormat hook defaults to DMY format during server-side rendering and updates to the correct format on client mount.
Controlled Input¶
The component is fully controlled - the parent's value prop is the source of truth, and all changes are communicated via onChange.