javascriptreactjstypescriptformsnext.js

React: “key” Prop Being Spread into JSX Using getInputProps from @conform-to/react


I am encountering an issue where React complains about a key prop being spread into a JSX element, even though I’m not explicitly passing it as part of my props.

Here’s the relevant code snippet:

<Input
  className="mb-[20px]"
  label="First Name"
  errorMessage={fields.firstName.errors?.at(0)}
  {...getInputProps(fields.firstName, { type: "text" })}
/>

The error message:

A props object containing a "key" prop is being spread into JSX:
  let props = {key: someKey, className: ..., label: ..., errorMessage: ..., required: ..., id: ..., name: ..., form: ..., type: ..., minLength: ...};
  <Input {...props} />
React keys must be passed directly to JSX without using spread:
  let props = {className: ..., label: ..., errorMessage: ..., required: ..., id: ..., name: ..., form: ..., type: ..., minLength: ...};
  <Input key={someKey} {...props} />

Conform documentation: https://conform.guide/api/react/getInputProps

Here is the full form:

"use client";

import { useActionState } from "react";
import { getFormProps, getInputProps, useForm } from "@conform-to/react";
import { getZodConstraint, parseWithZod } from "@conform-to/zod";

import { signUpWithDetails } from "@/features/authentication/actions";
import { SignUpSchema } from "@/features/authentication/schemas/sign-up";

import { Input } from "@/common/components/Form/Input/Input";
import { Button } from "@/common/components/Form/Button/Button";
import { DatePicker } from "@/common/components/Form/DatePicker/DatePicker";

interface DetailsFormProps {
  email: string;
}

export const DetailsForm = ({ email }: DetailsFormProps) => {
  const [lastResult, action, pending] = useActionState(
    signUpWithDetails,
    undefined,
  );

  const [form, fields] = useForm({
    lastResult,
    constraint: getZodConstraint(SignUpSchema),
    onValidate({ formData }) {
      return parseWithZod(formData, { schema: SignUpSchema });
    },
    shouldValidate: "onSubmit",
    shouldRevalidate: "onInput",
  });

  return (
    <form className="w-full" action={action} {...getFormProps(form)}>
      <input type="hidden" name="email" value={email} />
      <Input
        className="mb-[20px]"
        label="First Name"
        errorMessage={fields.firstName.errors?.at(0)}
        {...getInputProps(fields.firstName, { type: "text" })}
      />
      <Input
        className="mb-[20px]"
        label="Last Name"
        errorMessage={fields.lastName.errors?.at(0)}
        {...getInputProps(fields.lastName, { type: "text" })}
      />
      <DatePicker
        className="mb-[20px]"
        label="Date of Birth"
        max={new Date().toJSON().slice(0, 10)}
        errorMessage={fields.dateOfBirth?.errors?.at(0)}
        {...getInputProps(fields.dateOfBirth, { type: "date" })}
      />
      <Input
        className="mb-10"
        label="Password"
        errorMessage={fields.password.errors?.at(0)}
        {...getInputProps(fields.password, { type: "password" })}
      />
      <Button
        className="w-full mb-6"
        label="Continue"
        variant="primary"
        type="submit"
        isLoading={pending}
      />
    </form>
  );
};

Solution

  • When using the key prop in React, it's important to place it before the spread operator.

    The key prop helps React identify and manage elements in a list more efficiently, especially when the list changes (like adding, removing, or reordering items).

    If you place the key after the spread operator, the value of key may get overridden by the properties in the object you're spreading. This can cause unexpected behavior, as React might not assign the key correctly

    Incorrect

     <Input
            className="mb-[20px]"
            label="Last Name"
            errorMessage={fields.lastName.errors?.at(0)}
            {...getInputProps(fields.lastName, { type: "text" })}
          />
    

    Correct

    <Input  
            className="mb-[20px]"
            label="Last Name"
            errorMessage={fields.lastName.errors?.at(0)}
            {...getInputProps(fields.lastName, { type: "text" })}
            key={fields.lastName.key}
          />
    

    Reference:https://github.com/vercel/next.js/issues/55642#issuecomment-1728746491