Ramiro Herrera
Ramiro Herrera

Reputation: 2919

Component cannot be used as a JSX component. Its return type 'Element[]' is not a valid JSX element

The following error occurs in the Todos component inside the TodoApp.tsx:

'Todos' cannot be used as a JSX component.
  Its return type 'Element[]' is not a valid JSX element.
    Type 'Element[]' is missing the following properties from type 'Element': type, props, key

Corresponding files:

TodoApp.tsx

function TodoApp() {
  return (
    <Body>
      <AppDiv>
        <Form />
        <Todos />
        <Footer />
      </AppDiv>
    </Body>
  );
}

Todos.tsx

function Todos(): JSX.Element[] {
  const todos = useSelector((state: RootState) => state.todos);
  const footer = useSelector((state: RootState) => state.footer);

  if (footer.hideAll) {
    if (footer.showCompleted) {
      return todos
        .filter((todo) => !todo.completed)
        .map((todo: any) => (
          <>
            <ul>
              <Todo todo={todo} />
            </ul>
          </>
        ));
    }
    return todos.map((todo) => (
      <>
        <div>
          <Todo todo={todo} />
        </div>
      </>
    ));
  }

  return todos.map(() => (
    <>
      <div></div>
    </>
  ));
}

Todo.tsx

type Todo = {
  todo: TodoProps;
};

const Todo = ({ todo }: Todo) : JSX.Element => {
  const [isEditing, edit] = useState<boolean>(false);
  const dispatch = useDispatch();

  if (!isEditing) {
    return (
      <TodoDiv>
        <Li
          key={todo.id}
          completed={todo.completed}
          onClick={() => dispatch(completeTodo(todo.id))}
          // style={{
          //   textDecoration: todo.completed ? "line-through" : "none"
          // }}
        >
          {todo.text}
        </Li>
        <TodoBttns>
          <Button edit onClick={() => edit(!isEditing)}>
            <img src={editBttn} alt="Edit Button" />
          </Button>
          <Button delete onClick={() => dispatch(deleteTodo(todo.id))}>
            <img src={deleteBttn} alt="Delete Button" />
          </Button>
        </TodoBttns>
      </TodoDiv>
    );
  } else {
    return (
      <FormEdit>
        <InputForm key={todo.id} {...{ todo, edit }} />
      </FormEdit>
    );
  }
};

and the TodoProps interface is defined as follows

interface TodoProps {
  text: string;
  completed: boolean;
  id: string;
}

I have tried fixing it by wrapping the map of items with fragments, but the error is still thrown. The only thing that as of now is fixing the issue is declaring at the top of Todos.tsx the function as any: function Todos(): any

As a side note: I'm using StyledComponents, but I don't think the issue is related to the library.

Upvotes: 251

Views: 610396

Answers (25)

Hekmat
Hekmat

Reputation: 521

To fix this problem add this line to .tsconfig.json file

"compilerOptions": {
    ....,
    "paths": {
      ....,
      "react": [ "./node_modules/@types/react" ]
    }
 }

Upvotes: 0

himayan
himayan

Reputation: 842

You need to return a JSX Element, not an array. Wrapping the whole component is a solution, but you need to do it outside of the map/filter function.

// Todos.tsx

function Todos(): JSX.Element {
  const todos = useSelector((state: RootState) => state.todos);
  const footer = useSelector((state: RootState) => state.footer);

  if (footer.hideAll) {
    if (footer.showCompleted) {
      return (
        <>
          {todos.filter((todo) => !todo.completed).map((todo, index) => (
            <ul key={index}>
              <Todo todo={todo} />
            </ul>
           ));
          }
        </>
    }
    return (
      <>
        {todos.map((todo, index) => (
          <div key={index}>
            <Todo todo={todo} />
          </div>
        ));
        }
      </>
  }

  return (
    <>{todos.map((_, index) => <div key={index} />)}</>
  );
}

Upvotes: 18

Tugay Yaldız
Tugay Yaldız

Reputation: 749

For me, it was due to the VS Code TypeScript version. Changing it to the workspace version fixed the problem.

enter image description here

Upvotes: 0

Webber
Webber

Reputation: 5484

You could just specify the JSX dialect by changing the jsx option in tsconfig.json to react-jsx like so:

{
  "compilerOptions": {
    "jsx": "react-jsx",
  }
}

This is an alternative answer to Or Nakash's asnwer above, which suggests adding a path like so:

{
  "compilerOptions": {
    "paths": {
      "react": ["./node_modules/@types/react"]
    }
  }
}

Upvotes: 8

Jarda
Jarda

Reputation: 31

In my case author of react-rating decided to use specific version of react types inside his package and this did not match with version chosen by me used in the rest of the project.

I solved this by using overrides in package.json https://docs.npmjs.com/cli/v10/configuring-npm/package-json#overrides

Upvotes: 0

IAmCoder
IAmCoder

Reputation: 3442

Instead of importing like this:

import Picker from '@react-native-picker/picker

Change the import to:

import { Picker } from '@react-native-picker/picker

Without the curly braces it expects Picker to be the default export: https://stackoverflow.com/a/36796281/4279006.

Upvotes: 1

Pritam
Pritam

Reputation: 1400

In case anyone is facing issue with React + Typescript stack, try adding below setting in tsconfig.json. It worked for me.

"allowSyntheticDefaultImports" : true

Upvotes: 14

Victor Eke
Victor Eke

Reputation: 465

You probably shouldn't get this error if you're running on the latest TS version @types/react, @types/react-dom, etc. Here's a thread for anyone interested in knowing the cause of this issue:

I have a unique use-case in which I do not have access to upgrade any packages (working with an online editor). A quick hack is ignoring the warning using the @ts-expect-error directive.

For example:

import Component from "./Component"

export default function App({children}: {children: React.ReactNode}) {
  return (
   <div className="App">
     {/* @ts-expect-error */}
     <Component />
     {children}
   </div>
 )
}

Upvotes: 0

Talha
Talha

Reputation: 880

I'm facing the same issue about this error. I add the below code to my package.json file.

"resolutions": {
  "@types/react": "17.0.2",
  "@types/react-dom": "17.0.2",
},

Then, Run yarn install on a terminal.

It works for me.

Upvotes: 13

Lasf
Lasf

Reputation: 2582

Here's another possibility. TL;DR: look for node_modules folders outside your project root (see node -e "console.log(global.module.paths)") and delete them.

Node searches for packages in node_modules folders over several paths, often going from your project root to your system root. See node -e "console.log(global.module.paths)" for where it'll look. Let's say you installed, for example, @types/[email protected] (or whatever version) in your project root – you'll see it your node_modules there. But then you (perhaps accidentally, perhaps transitively) also installed @types/[email protected] in one of those other search paths instead of your project root.

When Node goes searching, it finds both types dependencies: 17.0.2 and 18.2.0, and when it finds two differing React major versions for types it can cause problems because Typescript types packages have a bad habit of using * dependency ranges and the packaging default is to simply install the latest version. In this example, your React 17 project would fail to compile with weird errors like like 'Todos' cannot be used as a JSX component because it is being compiled using the React 18 types. See here for a painful example of mixed 17 and 18 types. I'm sure there could be conflicts between other versions as well, and also this doesn't just apply to React, it could apply to any library with drastically changing APIs/types and enthusiastic developers.

There are many variations of this example, but the solution is to delete the node_modules folders that are outside your project root along the Node search path, and then make sure you actually have the packages you need in the node_modules in your project root. This will remove the React types version conflicts and you should compile with just your regular dumb easily-solvable errors instead.

You may have a reason for keeping packages outside your project root, in which case you either know what you're doing or good luck – Typescript packages do not resolve well at all in our current ecosystem. If you have multiple projects with differing React versions, place them in your filesystem so that their Node search paths don't conflict, and make sure you don't have a node_modules sitting in your system or user root somewhere where it'd find them.

Upvotes: 2

Mitchell MM
Mitchell MM

Reputation: 49

"paths": {
  "@/*": [
    "./*"
  ],
  "react": [ "./node_modules/@types/react" ]
},

add this to your tsconfig.json file, it works

Upvotes: 4

Manikanth Prasad
Manikanth Prasad

Reputation: 9

When you import anything then try to import in Curly Braces.

for example when we import Box in React Component try to write

import {Box} from "@mui/material" instead of 
import Box from "@mui/material"

Upvotes: -2

Ankur Kedia
Ankur Kedia

Reputation: 3853

You might neeed to add the @types/react and @types/react-dom to your package.json.

Upvotes: 5

pradeep murugan
pradeep murugan

Reputation: 655

you returned something when your condition is true but you didn't return anything when the condition is not satisfied. This is what the error says exactly.

Upvotes: -1

Roger Perez
Roger Perez

Reputation: 3129

I followed some of the steps above where I had to declare the types within pacakge.json but that did not work for me.

I had to manually update the yarn.lock file to React version 16. It was displaying version 18.x in the lock file.

You may also want to update the resolved url to match but test before doing so.

version "18.0.28" to version "16.14.15"

"@types/react@*":
  version "16.14.15" 
  resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce"
  integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==
  dependencies:
    "@types/prop-types" "*"
    "@types/scheduler" "*"
    csstype "^3.0.2"

Upvotes: -1

Or Nakash
Or Nakash

Reputation: 3329

Add the following code

"paths": {
   "react": [ "./node_modules/@types/react" ]
 }

to tsconfig.json file, in compilerOptions

Here is how my code looks like now:

{
  "compilerOptions": {
    "jsx":"react",
    "strict": true,
    "paths": {
      "react": [ "./node_modules/@types/react" ]
    }
  }
}

It is telling the typescript compiler that when it encounters a reference to the "react" module, it should look for the actual implementation in the "./node_modules/@types/react" directory.

Upvotes: 211

Another common cause of the "Component cannot be used as a JSX component" error is when we return anything other than a JSX element or null from a component, or forget to return a value.

The App component returns undefined because we have placed our return statement on one line and the JSX code on the next without using parentheses.

We aren't allowed to return undefined from a component, so the error occurs.

Upvotes: 1

Ihor
Ihor

Reputation: 129

Sometimes this error can occur when you use the switch case syntax to render your component.

For example:

switch (loading) {
    case "pending":
      return <span>Pending</span>;
    case "succeeded":
      return <span>Succeeded</span>;
    case "failed":
      return <span>Failed</span>;

Sometimes your loading status can mistakenly have a value that you didn't specify in the case, so your component will return undefined. Therefore, you should add a default value:

default: return <span>¯\_(ツ)_/¯</span>;

Upvotes: 5

sambalicious
sambalicious

Reputation: 1611

You need to install types for react npm install @types/react or yarn add @types/react

Upvotes: 3

Kiril Dobrev
Kiril Dobrev

Reputation: 851

This answer is related to same problem on react-native. Adding typescript to my newly created react-native app and moving App.js to App.tsx, I met the same error for component named Section. The solution for me was a component type React.Fc<{...}>. This is the problematic Section component after the fix when all errors disappeared and my app started as expected.

 import React, { ReactNode } from 'react';
 import {
   SafeAreaView,
   ScrollView,
   StatusBar,
   StyleSheet,
   Text,
   useColorScheme,
   View,
 } from 'react-native';
 
 import {
   Colors,
   DebugInstructions,
   Header,
   LearnMoreLinks,
   ReloadInstructions,
 } from 'react-native/Libraries/NewAppScreen';
 
 const Section: React.FC<{  
  children: ReactNode;   
  title: string; 
}> = ({children, title}) => {
   const isDarkMode = useColorScheme() === 'dark';
   return (
     <View style={styles.sectionContainer}>
       <Text
         style={[
           styles.sectionTitle,
           {
             color: isDarkMode ? Colors.white : Colors.black,
           },
         ]}>
         {title}
       </Text>
       <Text
         style={[
           styles.sectionDescription,
           {
             color: isDarkMode ? Colors.light : Colors.dark,
           },
         ]}>
         {children}
       </Text>
     </View>
   );
 };

Upvotes: 0

Terry Windwalker
Terry Windwalker

Reputation: 1888

There's a possibility that you have installed (and probably failed) a new package, which could also cause this error.

If that's the case (that you have recently installed and failed or cancelled a package), you can delete node_modules and run npm i or yarn install then try again.

Upvotes: 5

Daniel Danielecki
Daniel Danielecki

Reputation: 10502

For a scenario when someone has upgraded to later React, e.g., 16 -> 17 it could start happening.

Now, while looking for @types/react in your project, you could notice many npm packages have @types/react : "*",. The solution to get rid off the problem, in that scenario, would be to add into your package.json:

"resolutions": {
  "@types/react": "^17.0.38"
}

Upvotes: 44

Cyclion
Cyclion

Reputation: 774

In my case I forgot to put back ticks after styled(PreviousStyledElement); After I put them the code started to work.

export const MyNewStyledElement = styled(PreviousStyledElement)``;

Upvotes: -1

em_code
em_code

Reputation: 438

In my case, it was a forgotten import. Basically copy-pasted some of my code and forgot to import one of the components and this was the error message I get.

Upvotes: 4

Alex Wayne
Alex Wayne

Reputation: 186984

A component needs to return a single root element. You can use fragments to package an array of elements as a single element, by using the fragment as that single root element.

So this does nothing:

function Todos(): JSX.Element {
  return todos.map(todo => (
    <>
      <li>{todo.task}</li>
    </>
  )
}

Because it's now returning an array of [<><li/></>, <><li/></>, ...]. That fragment needs to be the single root element.

You need to use the fragment like this:

function Todos(): JSX.Element {
  return <>{
    todos.map(todo => <li>{todo.task}</li>)
  }</>
}

You nest all returned JSX in one single fragment.

Using that pattern you may end up with somehting like this:

function Todos(): JSX.Element {
  const todos = useSelector((state: RootState) => state.todos);
  const footer = useSelector((state: RootState) => state.footer);

  if (footer.hideAll) {
    if (footer.showCompleted) {
      return <>{
        todos
          .filter((todo) => !todo.completed)
          .map((todo: any) => (
            <ul>
              <Todo todo={todo} />
            </ul>
          ))
      }</>
    }
    return <>{
      todos.map((todo) => (
        <div>
          <Todo todo={todo} />
        </div>
      ))
    }</>
  }

  return <>{
    todos.map(() => (
      <div></div>
    ))
  }</>
}

// Works without error
<Todos />

Note how each return statement returns just one JSX.Element: the fragment.

Playground

Upvotes: 215

Related Questions