Avatar

Hola, I'm Julia.

HTML Time Input Field

#formik #forms #react #react-datepicker #typescript #yup

4 min read

I'm currently working on a React app that requires the use of forms. It recently came to be that I needed to extend one of the forms to allow a user to input a date and time. As this was going to be a fairly short lived project, I didn't want to spend too much time having to build custom date and time input components from scratch.

Doing a quick search surfaced the React Datepicker package as a good option for what I needed, namely with the use of the Input Time option. My initial thoughts were that using a single popup to choose the date and time would allow for a better user experience. This would also simplify the creation of the DateTime object which could then be saved directly to the database without needing further manipulation. Win-win.

As I started integrating the Input Time component however, I noticed that the time input was not playing nicely at all with what I needed it to do. Looking into it further, I realised that the <input type='time'> HTML element is not recognised by Safari and Internet Explorer (where time inputs degrade gracefully to a <input type='text'> instead). Whilst this doesn't pose a big problem style-wise, it is a bigger problem from a data format perspective, as I needed to be sure that the user input was capable of being parsed as time for further downstream calculations.

I decided that this would need to be done through a combination of explicit instructions to the user and data validation on the input field. This meant having to break from the Input Time React Datepicker component and having separate input fields for the date and time instead. This would be slightly more work, but would offer greater clarity for the user and greater control over how instructions to the user could be communicated.

To try to ensure the data would be entered in the required format, this was the setup I went with.

<input {...props} type="time" pattern="[0-9]{2}:[0-9]{2}" minLength={5} maxLength={5} />

I use Formik with Yup validation for forms, so I also added time: Yup.string().required() to be sure this time input field is filled in before allowing the user to submit the form. P.S. the time key in the Yup validation refers to the name of the input field, not the type.

From a user communication perspective, I included a placeholder (which comes through via props ) and an input label. In summary, this is how I render a time input field in my app...

<TimeInput label="Time (24 hour format)" name="time" placeholder="Enter start time in 24 hour format e.g. 08:00" />

...and this is what the TimeInput component looks like. (Note: I style my components with Tailwind CSS, so that's where the utility classes in className comes from.)

import React from 'react'
import { FieldLabel } from './FieldLabel'

export interface TimeInputProps {
  name: string
  placeholder: string
  label?: React.ReactNode
}

export const TimeInput: React.FC<TimeInputProps> = ({ label, ...props }) => {
  return (
    <div className="flex flex-col mt-4">
      {label && (
        <FieldLabel htmlFor={props.name} className="mb-2">
          {label}
        </FieldLabel>
      )}
      <input
        className="appearance-none px-6 py-5 focus:outline-none"
        {...props}
        type="time"
        pattern="[0-9]{2}:[0-9]{2}"
        minLength={5}
        maxLength={5}
      />
    </div>
  )
}

FieldLabel is a custom component I use for rendering standard HTML <label> elements. If you're curious, this is what it looks like.

import React from 'react'

export type FieldLabelProps = React.ComponentProps<'label'>

export const FieldLabel: React.FC<FieldLabelProps> = React.memo(({ children, ...props }) => (
  <label {...props}>{children}</label>
))

One other thing. The default time input field style includes a clock icon that pops up a time selector menu. I didn't want this default style, so I hid it using the following CSS setting.

[type='time']::-webkit-calendar-picker-indicator {
  opacity: 0;
}

© 2016-2024 Julia Tan · Powered by Next JS.