DEV Community

Boryamba
Boryamba

Posted on

Custom Formik components with Typescript and Chakra UI

Resources:

foreword

Hello everyone.

I will be using create next-app throughout the post but everything in here is appliable to regular React (CRA) applications.

I expect you to be familiar with Chakra UI and Formik

Let's get started =)



Let's start with creating a new NextJS app:

yarn create next-app MyNextApp --typescript

Open created folder in your IDE, then install Formik and Chakra UI

yarn add @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4 formik
Enter fullscreen mode Exit fullscreen mode

Next we create a new folder components and a file named CustomInput.tsx

Inside that file create a new Functional component named CustomInput:

export const CustomInput: FC = ({ label, ...props }) => {
  const [field, meta] = useField(props);

  return (
    <FormControl isInvalid={meta.touched && !!meta.error}>
      <FormLabel>
        {label}
      </FormLabel>
      <Input {...field} />
    </FormControl>
  );
}
Enter fullscreen mode Exit fullscreen mode

At this moment Typescript will yell at us saying it doesn't know what're the label and props, we need to explain to that silly what those things are. So we need an interface which can be written two ways: either by extending JSX.IntrinsicElements['input'] interface or by intersecting FieldHookConfig<string> type:

// intersecting
interface ICustomFieldProps {
  label: string;
}

// FieldHookConfig accepts value type as an argument,
// it is 'string' for input elements
export const CustomInput: FC<FieldHookConfig<string> & ICustomFieldProps> = ({ 
  label,
  ...props
}) => { 
...
}

// extending
interface ICustomFieldProps extends JSX.IntrinsicElements['input'] {
  label: string;
  name: string;
}

export const CustomInput: FC<ICustomFieldProps> = ({ label, ...props }) => { 
...
}
Enter fullscreen mode Exit fullscreen mode

Choose whatever way you like.

Now let's create our form. Open pages/index.tsx file, delete everything in between div and replace div with Formik component.

Add some dummy initialValue and onSubmit to Formik component, place <Form> and <CustomInput /> inside <Formik>:

const Home: NextPage = () => {
  return (
    <Formik initialValues={{name:''}} onSubmit={(val) => alert(val.name)}>
      <Form>
        <CustomInput name='name' label='name' />
        <Button type='submit'>Submit</Button>
      </Form>
    </Formik>
  )
}

export default Home
Enter fullscreen mode Exit fullscreen mode

Ta-da. We now have a form built with Chakra UI and managed by Formik.

But. If you're using NextJS and you open devtools console, you will see a big fat error:

Warning: Prop `id` did not match.

We can fix this by adding id to our form elements explicitly. Inside CustomInput.tsx add id attribute to Input and FormLabel components and htmlFor attribute to FormLabel:

export const CustomInput: FC<IFormikFieldProps & FieldHookConfig<string>> = ({
  label,
  ...props
}) => {
  const [field, meta] = useField(props);

  return (
    <FormControl isInvalid={meta.touched && !!meta.error}>
      <FormLabel
        id={`${props.id}-${props.name}-label`}
        htmlFor={`${props.id}-${props.name}-input`}
      >
        {label}
      </FormLabel>
      <Input 
        {...field}
        id={`${props.id}-${props.name}-input`} 
      />
    </FormControl>
  );
};
Enter fullscreen mode Exit fullscreen mode

Cool. It's done.

notes:
  • for select, you must pass props.children to Chakra UI's Select component explicitly:
<Select {...field} id={`${props.id}-${props.name}-select`}>
  {props.children}
</Select>
Enter fullscreen mode Exit fullscreen mode
  • you can add FormErrorMessage component, too

Thanks for reading =)

Discussion (0)