opsinjs
ComponentsPatternsAsk users for…

Ask users for a date of birth

Three text fields, not a calendar. First, the question of whether an age band would have done instead.

When to use

Ask for a date of birth when a decision in your product genuinely depends on knowing the reader's exact date rather than their approximate age:

  • Identity matching. Linking a record to a clinical system that keys on it.
  • Age-dependent reference ranges where the boundary is sharp. Some paediatric measures change materially within a year.
  • Eligibility with a legal date boundary, such as a screening programme.

For everything else, ask for an age band. Personalising a general adult range, choosing content, or checking that a reader is over eighteen do not require a date; they require a decade or a threshold. An age band is easier to answer, cheaper to store, and less useful to anyone who should not have it.

When not to use

  • You only need to know that they are an adult. Ask that. A yes/no or a single band answers it without collecting a birth date.
  • You are asking for a recent date, such as when a symptom started or when a dose was taken. Those have completely different affordances: relative options ("today", "yesterday") beat any date entry. See Symptoms.
  • You are asking for a future date. An appointment is a picker problem, and a calendar is the right control there. The argument below is specifically about dates decades in the past.
  • You want form-level behaviour. Form design.

How it works

Diagram source (mermaid)
flowchart TD
A["We need age information"] --> B{"Does a decision need the exact date?"}
B -->|"no"| C["Ask for an age band and stop"]
B -->|"yes"| D["Three separate text fields: day, month, year"]
D --> E["Each labelled, numeric keyboard, bday-* autofill tokens"]
E --> F{"Complete?"}
F -->|"partially"| G["Name the missing part, keep what was entered"]
F -->|"yes"| H{"Valid calendar date, not in the future?"}
H -->|"no"| I["Say specifically what is wrong"]
H -->|"yes"| J["Accept; echo it back in full on review"]

Three text fields, not a calendar. This is the core recommendation. A date picker is optimised for choosing a date near today; reaching 1962 through one requires many interactions, and the widget's month grid is meaningless for a date the reader knows by heart and could type in four seconds. The three-field approach is what GOV.UK and the NHS service manual both document publicly, and the reasoning holds outside those contexts.

Details that matter:

  • Order the fields for the reader's locale. The order is day, month, year in most of the world; month, day, year in the United States. Do not reorder the labels to match a stored format.
  • Every part is labelled visibly: "Day", "Month", "Year". Not placeholders.
  • Accept a month as digits or as a name. Someone typing "March" or "Mar" has answered the question.
  • Four-digit years only. Two-digit year entry is ambiguous at exactly the wrong place: 26 is 1926 for some readers of a health product and 2026 for others.
  • Do not auto-advance between the parts. It is disorienting, it breaks correction, and it is a common cause of a transposed digit going unnoticed.
  • Use autocomplete="bday-day", "bday-month", "bday-year" on the three fields, or "bday" on a single field where you use one. These are real tokens in the HTML specification's autofill list and are what WCAG 2.2 SC 1.3.5 points at.
  • Never derive and display an age the reader did not give you without showing the date it came from. A wrong age from a transposed digit is invisible; a wrong date is not.

Edge cases that are not edge cases

  • Unknown or partial dates are real. People adopted, displaced, or born where records are incomplete may know only a year. If your system can accept a year alone, say so; if it cannot, say that too rather than letting the reader invent a day.
  • Very old readers exist. A validation rule capping age at 100 will reject genuine users. Set physical bounds, not convenient ones.
  • Leap days are real. 29 February is a valid answer and a classic rejection.
  • The date does not change. Once given, it should be re-editable but never re-asked; WCAG 2.2 SC 3.3.7.

Content

The example hint shows three numerals because a date of birth is three answers, not one: 27, then 3, then 1985 are the Day, Month and Year fields below the question, never one string typed into a single box.

Do

Heading: "What is your date of birth?" Hint: "For example, 27 3 1985". Day, Month and Year fields below the heading. Reason, one line: "We use this to compare your readings to the range for your age."

Don’t

Label: "DOB*". Placeholder: "dd/mm/yyyy". A calendar icon that opens a picker starting at this month, with no keyboard entry path.

Do

Error: "Your date of birth must include a year." It names the missing part and leaves the day and month as entered.

Don’t

Error: "Invalid date" with all three fields cleared, so the reader starts again.

Accessibility

  • The three fields are a fieldset with a legend carrying the question, so each part is announced in context: "Date of birth, Day".
  • The hint precedes the inputs and is referenced by aria-describedby on the group, so it is heard before the reader starts typing rather than after.
  • Errors name the part. "Enter a year" is actionable; "invalid" is not (WCAG 2.2 SC 3.3.1 and 3.3.3).
  • inputmode="numeric" on each field so a phone keypad appears, with the fields still accepting a typed month name where you support it.
  • No auto-advance, so a reader using a screen reader or switch access can review and correct each part.
  • Nothing is cleared on error. Re-entry of correct parts is a WCAG 2.2 SC 3.3.7 failure and a reliable way to lose a reader.
  • If you offer a calendar as an alternative, it is additive, keyboard operable, and never the only path.

Research

Updates to this page

Last read through against the system on 2026-09-20. Due for review every 12 months; expiry is reported by pnpm run check:freshness.

On this page