Chakra UI with 'redwoodjs/forms' elements

I was following chapter 3 of the redwood tutorial, that explains how to use components from ‘redwoodjs/forms’ to validate data from html forms. However, I had installed Chakra UI for styling my application. I’m a beginner so maybe I’m missing something, but I don’t see a path forward to get the benefits of validation with ‘redwoodjs/forms’ with the styling of Chakra. I’m happy to help implement a solution with some guidance.

Thanks,
Shane K

This comment seems to suggest it should be possible: Add Chakra UI integration by TimKolberger · Pull Request #3715 · redwoodjs/redwood · GitHub

I’ve never tried it myself though. So no idea how it’d work. @keith.t.elliott I know you’ve used Chakra. Any experience with integrating it with rw forms? @jacebenson you “liked” the GH issue comment. Have you tried this?

I found that it’s an “either or” situation.

Either you get redwood’s forms, OR you use react-hook-form with chakra.

You have to include react-hook-form, and pass register to the fields. I feel like my code is a mess and overly complicated, but here’s an example of how I’m making my forms.

I load up all the things I need and pass them into a “FormComponent” and on that, I set the register and the like for each form.

For me the thing that took me the longest to figure out was understanding I needed to “register” the field with react hook form.

Hi all! I’ve sent a message to the fine folks at Chakra UI to see if they’re available to take a look at this.

Additionally, chakra UI has some documentation on how to use it with react hook form. Since chakra is component-based I don’t think it is possible to use it directly with redwoods automatically registered form inputs.

1 Like

The link @KrisCoulson sent should give you an idea on how to integrate Chakra UI with React Hook Form. If we’re talking about integrating Chakra UI with Redwood Forms, here’s a little something I did: example-todo/AddTodoControl.js at main · nikolovlazar/example-todo · GitHub.

Basically, Chakra UI has an as prop that can be used to render a Chakra component as something else. In this case, I’m rendering a Chakra Input component as a Redwood TextField component.

In case the as component doesn’t cut it, there’s also the Chakra Factory Function. We usually use the factory function to wrap Next.js’s Image component so we can have Chakra UI styling with the image optimization from Next.js. There’s a shouldForwardProp property that you can define in the options (2nd argument of factory function) if you want to precisely define which props will be forwarded to the other component (ex. the nextjs image).

I think the as prop and/or the Chakra Factory Function will help you integrate Redwood Forms with Chakra UI. Let me know if I should provide more info on this.

6 Likes

@nikolovlazar Thank you so much for jumping in to help here!

1 Like

To make your Chakra UI forms cleaner you could use: Form - Saas UI
It’s a project I’m building on top of Chakra UI, the forms use Hook Form under the hood and has a similar API as Redwood forms.

If there is any way I can help with implementing Chakra with the Redwood forms, let me know.

1 Like

@nikolovlazar Huge thank you :pray: Just the best.

@Eelco sounds like you and @shanemlk should team up! You’d have the support and collaboration of both the Redwood Core Team and Chakra UI team.

And we :heart: Contributors:

FYI: we are very heads down on v1 Launch Week right now. Don’t wait up on us! But don’t expect too much until after Launch Week

1 Like

Great, @shanemlk are you on the Discord? We can talk there to see how to continue this.

Good luck with the release @thedavid , exciting!

1 Like

Thanks for all the input everyone. I did some testing.

  1. First I converted the form to use chakra elements with the “as=” prop pointing to redwood form components. It styles the whole form correctly and still functions like a redwood form, however, after submitting and receiving errors, the error styling reverts.
  2. I followed the Chakra UI + React Hook Form code. This also styles it correctly, and handles errors client side great, but out of the box it has no way to render redwood service validation on the backend.

Path #1 seems like the most viable option, to keep the redwood forms as close to the ecosystem as possible, it might just require some class/styling updates.

@Eelco my name on discord shane_monk. Happy to talk more there.

2 Likes

@shanemlk can you share the issue in Path #1 with me? I’d like to take a better look at it.

@nikolovlazar

1 Like

@shanemlk the FormControl component controls the error with the isInvalid prop. Try setting the isInvalid prop to the FormControl to the error property for that input in the errors. As the name suggests, it accepts a boolean value.

I updated it to capture the name of the input on error with useState(). It then sets the isInvalid prop on the FormControl. This is working great now with backend validation. It doesn’t display the errors below the input, but it highlights them red, so that’s good enough for me.

Thanks for your help!

1 Like

@shanemlk I took a stab at this and the reason that the error message is not showing the way you are using it is because FormErrorMessage expects the error message as children so passing the FieldError. Doing it like this worked for me, the className might not be necessary, I was just using it like that.

<FormErrorMessage>
  <FieldError name={name} className="rw-field-error" />
</FormErrorMessage>
1 Like

Has anyone been able to get this to work with radio buttons? When trying Radio as={RadioField} as per below, I end-up with the following error:

          <FormControl as="fieldset">
            <FormLabel htmlFor="type">Event type</FormLabel>
            <RadioGroup defaultValue="CLASS">
              <HStack spacing="24px">
                <Radio value="Test1">Test1</Radio>
                <Radio
                  as={RadioField}
                  isInvalid={errorField == 'type'}
                  id="event-type-0"
                  name="type"
                  defaultValue="CLASS"
                  defaultChecked={props.event?.type?.includes('CLASS')}
                  validation={{ required: true }}
                >
                  CLASS
                </Radio>
              </HStack>
            </RadioGroup>
            <FormErrorMessage>
              <FieldError name="type" className="rw-field-error" />
            </FormErrorMessage>
          </FormControl>

I have the following imports

import {
  FormControl,
  FormLabel,
  FormErrorMessage,
  Container,
  RadioGroup,
  HStack,
  Radio,
} from '@chakra-ui/react'

import { FieldError, Form, Submit, useForm, RadioField } from '@redwoodjs/forms'

I managed to fix this by registering the Chakra Radio component as a new form component as follows.

import type { RadioProps } from '@chakra-ui/radio'
import { Radio } from '@chakra-ui/react'

import { Controller, RegisterOptions, useErrorStyles } from '@redwoodjs/forms'

interface Props extends RadioProps {
  validation?: RegisterOptions
  errorClassName?: string
}

const ChakraRadioField = (props: Props) => {
  const {
    name,
    className,
    errorClassName,
    defaultValue,
    validation,
    style,
    ...propsRest
  } = props

  const { className: componentClassName, style: componentStyle } =
    useErrorStyles({
      className: className,
      errorClassName: errorClassName,
      name: name,
    })

  return (
    <Controller
      name={name}
      defaultValue={defaultValue}
      rules={validation}
      render={({ field: { onChange, onBlur, value, name, ref } }) => (
        <Radio
          {...propsRest}
          checked={value}
          onChange={onChange}
          onBlur={onBlur}
          ref={ref}
          name={name}
          className={componentClassName}
          style={{ ...componentStyle, ...style }}
        />
      )}
    />
  )
}

export default ChakraRadioField

And then importing and using as follows within FormControl

                <ChakraRadioField
                  value="CLASS"
                  id="event-type-0"
                  name="type"
                  defaultValue="CLASS"
                  defaultChecked={props.event?.type?.includes('CLASS')}
                ></ChakraRadioField>
1 Like

Thanks for sharing your solution @0x1a4f7d58

1 Like

Hi @aguscha333 Thanks for this. Were you able to get client side validations to show or just server side? I have the following yet it’s not clear how I can trigger isInvalid={true} on the client to display Email is required <===.

Is the suggestion here that one just goes with server side validations?

        <FormControl isInvalid={errorField == 'email'}>
          <FormLabel htmlFor="email">Email address</FormLabel>
          <Input
            as={TextField}
            name="email"
            validation={{
              required: {
                value: true,
                message: 'Email is required <===',
              },
            }}
          />
          <FormErrorMessage>
            <FieldError name="email" />
          </FormErrorMessage>
        </FormControl>