Reputation: 2919
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
Reputation: 521
To fix this problem add this line to .tsconfig.json file
"compilerOptions": {
....,
"paths": {
....,
"react": [ "./node_modules/@types/react" ]
}
}
Upvotes: 0
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
Reputation: 749
For me, it was due to the VS Code TypeScript version. Changing it to the workspace version fixed the problem.
Upvotes: 0
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
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
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
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
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
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
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
Reputation: 49
"paths": {
"@/*": [
"./*"
],
"react": [ "./node_modules/@types/react" ]
},
add this to your tsconfig.json file, it works
Upvotes: 4
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
Reputation: 3853
You might neeed to add the @types/react
and @types/react-dom
to your package.json
.
Upvotes: 5
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
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
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
Reputation: 21
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
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
Reputation: 1611
You need to install types for react npm install @types/react
or yarn add @types/react
Upvotes: 3
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
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
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
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
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
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.
Upvotes: 215