React-Typescript: This JSX tag's 'children' prop expects a single child of type 'Element | undefined', but multiple children were provided

Viewed 71027

I am using react-typescript for my app. For styling I am using Style component. I have decided I will create my custom Hooks Input and Password input. So I first created Input wrapper where I added necessary input fields. After that I have created Password input field. i extended my Password input component props with Input component props. But when I am using Input component wrapper to my password component. I am getting error: This JSX tag's 'children' prop expects a single child of type 'Element | undefined', but multiple children were provided.

I don't know what I am doing wrong.

This is my Input wrapper component

import React from "react";
import styled from "styled-components";
import ErrorBoundary from "components/errorBoundary";

const Container = styled.div`
  position: relative;
`;

const Label = styled.label<{ minimized?: boolean }>`
  display: block;
  color: ${({ theme }) => theme.inputLabelColor};
  font-weight: 400;
  font-size: ${({ minimized }) => (minimized ? "11px" : "16px")};
  margin-bottom: 5px;
  letter-spacing: -0.2px;
  position: absolute;
  transform: translateY(${({ minimized }) => (minimized ? "9px" : "16px")});
  left: 20px;
  top: 0;
  pointer-events: none;
  transition: transform 150ms linear, font-size 150ms linear;
`;

const Error = styled.p`
  color: ${({ theme }) => theme.errorTextColor};
`;

const Description = styled.p`
  color: blue;
`;

export interface IInputWrapperProps {
  label?: string;
  required?: boolean;
  minimizedLabel?: boolean;
  description?: string;
  error?: string;
  wrapperStyle?: React.CSSProperties;
  children?: JSX.Element;
}

export default ({
  label,
  error,
  description,
  children,
  required,
  wrapperStyle,
  minimizedLabel
}: IInputWrapperProps) => {
  return (
    <ErrorBoundary id="InputWrapperErrorBoundary">
      <div style={wrapperStyle}>
        <Container>
          <Label minimized={minimizedLabel}>
            {label} {required && <span style={{ color: "red" }}> *</span>}
          </Label>
          {children}
        </Container>
        {description && <Description>{description}</Description>}
        {error && <Error>{error}</Error>}
      </div>
    </ErrorBoundary>
  );
};

This is my password component

import React, { useState } from "react";
import styled from "styled-components";
import eyeHide from "./eye-svgfiles/eyeHide.svg";
import eyeShow from "./eye-svgfiles/eyeShow.svg";
import InputWrapper, { IInputWrapperProps } from "../wrapper";
const Passwordinput = styled.input`
  border: 2px solid ${({ theme }) => theme.inputBorderColorFaded};
  background: ${({ theme }) => theme.inputBackgroundColor};
  display: block;
  border-radius: 5px;
  box-shadow: none;
  color: ${({ theme }) => theme.inputTextColor};
  height: 52px;
  width: 100%;
  margin: 0;
  padding: 1px 15px;
  &:focus {
    border: 2px solid ${({ theme }) => theme.inputBorderColor};
    outline: 0;
  }
`;
const Svg = styled.div`
  position: absolute;
  left: 50%;
  top: 65%;
  transform: scale(0.8);
`;

export interface IPasswordProps extends IInputWrapperProps {
  onChange: (i: string) => void;
  value?: string | undefined;
}
export default ({ onChange, value, label, error, ...rest }: IPasswordProps) => {
  const [state, setstate] = useState(false);
  return (
    <div>
      <InputWrapper label={label} error={error} {...rest}> //I am getting error in here 
        <Passwordinput
          label={label}
          type={state ? "text" : "password"}
          onChange={e => onChange(e.target.value)}
          value={value}
          error={error}
        />
        <Svg>
          <img
            onClick={() => setstate(state => !state)}
            style={{ cursor: "pointer" }}
            src={state ? eyeShow : eyeHide}
            alt="searchIcon"
          />
        </Svg>
      </InputWrapper>
    </div>
  );
};
10 Answers

Write your interface like this:

export interface IInputWrapperProps {
  label?: string;
  required?: boolean;
  minimizedLabel?: boolean;
  description?: string;
  error?: string;
  wrapperStyle?: React.CSSProperties;
  children?: JSX.Element|JSX.Element[];
}

in fact writing:

children: JSX.Element|JSX.Element[];

The children can be either a single JSX Element or an array of Elements.

It can be fixed with:

interface InputWrapperProps {
  ...yourProps,
  children?: React.ReactNode
}

Hope its helps !

To all React 18 adopters out there

If you just upgraded to React 18 and you're like me, you're adding React.PropsWithChildren<> everywhere since the types for props no longer imply children. If you're finding this thread, you likely ran into what I ran into where one random component gave this error for seemingly no reason, even though it's totally legal to have multiple elements as children.

SOLUTION:

Lots of different things can be "react children", including a lot of primitive types, and things like undefined, null, and false just don't render. My component was kind of like this:

<Container>
  {maybeNull && <Component notNullThing={maybeNull} />}
  {maybeNull2 && <Component notNullThing={maybeNull2} />}
  {maybeNull3 && <Component notNullThing={maybeNull3} />}
</Container>

Turns out, maybeNull technically had the type unknown, and that threw everything off. I fixed it with <>{maybeNull && <Component notNullThing={maybeNull} />}</> (because a single unknown is legal for some reason), though I could have done {maybeNull ? <Component notNullThing={maybeNull} /> : null} or something similar as well.

I think it's because the type of React.ReactNode is a big union that includes ReactFragment, which is recursively implemented as Iterable<ReactNode>. That's why a ReactNode can be an array of ReactNodes. However, unknown means you don't know which of the things in the union it is, so you don't know that it could be Iterable<ReactNode>. Some TypeScripty thing like that.

For me variant suggested by shuhat36 works fine. So I just added fragment for chilrens.

Example with error:

<div className="container">
   {renderTabs()}
   {renderButtons()}
</div>

And the same code but fixed:

<div className="container">
   <>
      {renderTabs()}
      {renderButtons()}
   </>
</div>

Correct practice is the following:

interface InputWrapperProps {
  ...yourProps,
  children?: ReactChild | ReactChild[] | ReactChildren | ReactChildren[];
}

Servus

One way to solve the error is to use a fragment to wrap the component's children. That worked for me.

For me what worked was to change the interface types to say children?: ReactElement|string and put a span around the child elements

Late to the party but just use

export interface YourType {
  type1: string,
}

const YourComponent = ({type1, children}: PropsWithChildren<YourType>) =>

For me it was a comment that I added inside tag. The comment was something like below. Looks like React treated this comment as a child.

{/* TODO: Pending work /*}

After removing this comment, it worked.

The problem is in '{'. inside consumer '{' must on new line.

Related