Next.js is evolving. Fairly recently, Next.js has added a new powerful routing mechanism which is used but adding files to the a app folder instead of the old pages folder. With new routing and new features like Server Components, comes also new errors to detect. This article will present solutions to four errors which are likely to happen the first hour of trying out the new Next.js features.
In This Article
- Exporting Async Server Component When Fetching Data
- Server Components Are Not the Same as Client Components
- Using Client-Side Hooks in Server Components
- Infinite Loading With Next.js 13 Development Server
- Should You Use Next.js 13?
Exporting Async Server Component When Fetching Data
Error: 'MyComponent' cannot be used as a JSX component. Its return type 'Promise' is not a valid JSX element
Reason to error: You are using an old version of TypeScript
Solution: Bump TypeScript or suppress the error with @ts-expect-error
Note: This error is a TypeScript error. Next.js 13 is TypeScript-first. which means you have to opt-out of it if you don't want it
To fetch data in server components, Next.js recommends trying out their built-in fetch API which is based on an RFC. Their example code for that uses an async Page function.
The problem arise when trying to use an async component like that in another page component. Doing that, will result in a TypeScript error.
'MyComponent' cannot be used as a JSX component.
Its return type 'Promise<Element>' is not a valid JSX element.
Type 'Promise<Element>' is missing the following properties from type 'ReactElement<any, any>': type, props, key
The error is thrown since the used TypeScript version doesn't recognize the async component. The issue has been solved recently in TypeScript version 5.1.3, but if you are bootstrapping templates or if you are checking out old git repositories you may have an outdated TypeScript version.
The solution is therefore to bump TypeScript version. If that for some reason cannot be done, you can suppress the warning with @ts-expect-error, which was the recommended solution from Next.js before it was fixed in TypeScript.
export default function Home() {
return (
<main>
{/* @ts-expect-error Server Component */}
<MyComponent />
</main>
)
}
Server Components Are Not the Same as Client Components
Error: You're importing a component that needs useState. It only works in a Client Component
Reason to error: You are using client-side only functionality in a Server Component
Solution: Add 'use client' directive to convert the Server Component to a Client Component
The most fundamental part when upgrading to Next.js 13 is to understand Server Components and Client Components. Traditionally in React and Single Page Applications (SPA) overall, the application has been rendered on the client side, in contrast to even more traditional websites that were rendered on the server. In modern programming, rendering is shared between the client and the server.
Next.js 13 gives you the full power to granularly fine-tune where you want to render each of your component, either on client or server, dependent on your needs. If you don't actively state that a Client Component should be a Client Component, you will run into a ReactServerComponentsError error.
ReactServerComponentsError:
You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
-[/path/to/file/MyComponent.ts:1:1]
1 | import { useState } from 'react'
The error above will show up when you have a component or hook which uses some client-side only functionality, but which isn't explicitly declared with a "use client" directive.
import { useState } from 'react'
const MyComponent = () => {
const [value, setValue] = useState()
// Some code...
return <div>{value}</div>
}
export default MyComponent
To fix the error, simply add the "use client" directive at the top of the file.
'use client'
import { useState } from 'react'
const MyComponent = () => {
const [value, setValue] = useState()
// Some code...
return <div>{value}</div>
}
export default MyComponent
Before you convert your component to a Client Component, take a minute to reflect over if you really need a Client Component or if you are better off with a Server Component. I think Next.js explains very well which one to use with a simple table.
A good rule of thumb, only use a Client Component if you need browser functionality or keeping a state
In short, one can say, components and hooks which uses useState, useReducer and useEffect are client-side components/hooks.
Secondly, components which relies on user interactions via the DOM tree are client-side, which means that DOM elements which have attached listeners like onClick and onChange belongs to Client Components.
A third case is also when the component or hook relies on a web API such as Geolocation API. There exist a lot of Web APIs, and they are often meant to run in a browser.
React Anti-Patterns and Best Practices - Do's and Don'ts
Dennis Persson ・ Feb 5 '23
Using Client-Side Hooks in Server Components
Error: Attempted to call the default export of /path/to/file/file.ts from the server but it's on the client
Reason to error: You are using client side functions in a Server Component
Solution: Convert the component to a Client Component or create a sub-client-component which uses the hook
In the previous header, we learned briefly what Server Components and Client Components, are and that we have to mark Client Components with the "use client" directive. That error was then resolved by using the directive, since Client Components can be used anywhere in Server Components. However, if the component you converted to a client component is a hook, you may see another error as well.
Let's say we have this hook:
'use client'
import { useState } from 'react'
const useSomeClientOnlyCode = () => {
const [value, setValue] = useState()
// Some code...
return { value }
}
export default useSomeClientOnlyCode
Then we use it in a Server Component as follow:
import useSomeClientOnlyCode from '@/hooks/useSomeClientOnlyCode'
const MyServerComponent = async () => {
const { value } = useSomeClientOnlyCode()
return(<div>{value}</div>)
}
If we run this code, we will see an error like this:
Error: Attempted to call the default export of /path/to/file/useSomeClientOnlyCode.ts from the server but it's on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.
at Component (./app/server-components/MyServerComponent.tsx:12:118)
at stringify (<anonymous>)
What is happening here, is that the hook contains client-side only code and is used in a Server Component. Whilst Next.js supports Client Components in Server Component, they don't support client functions or hooks to be used in Server Components.
Hooks are essentially just a part of a component, so if a hook uses client-side logic, then the whole component must be a client side Component as well.
The trivial way to solve this error is therefore to declare the component which uses the hook as a Client Component, by using the "use client" directive.
'use client'
import useSomeClientOnlyCode from '@/hooks/useSomeClientOnlyCode'
const MyServerComponent = async () => {
const { value } = useSomeClientOnlyCode()
return(<div>{value}</div>)
}
If you don't want to convert your whole component to a Client Component you can always lift out the parts of the component which utilizes the hook into a child component, and then make the child component a Client Component.
const MyServerComponent = async () => {
return(<div>
<MyClientComponentWhichUsesTheHook />
<div>Some other server rendered elements...</div>
</div>)
}
Infinite Loading With Next.js 13 Development Server
Error: Node.js development server does not work
Reason to error: Varies, in my case I had an async Client Component
Solution: In this case, remove the async keyword
Next.js 13 introduces a new app folder, which offers a new and improved way to perform routing. I have been using Next.js 13 a long time with the old pages folder without any issues with the development server or build, but as soon as I tried to use the new app folder I ran into severe problems. The development server wasn't working.
What happened in detail was that the web page never finished loading. Next.js hot reloading didn't work, and the page stopped responding. Even when I stopped the development server it continued to serve a non-working build on the port.
I read GitHub threads as Next.js dev server gets stuck after a while, requests just spin and browsed Stack Overflow for similar errors. Nothing could fix my problem. Changing ports didn't help, and neither did upgrading or downgrading versions of node or packages. Removing node_modules, .next folder and package-lock.json or swapping to Turbopack did nothing good.
It was really frustrating to troubleshoot when Next.js development server didn't work. And when I finally found the solution, the culprit wasn't more than five characters long. You recognize that feeling, don't you?
My mistake was a silly copy-paste mistake after having tested out to use fetch in a Server Component. Lesson learned there, look out so you don't have any Client Components with the async keyword. Only Server Components can be async in Next.js.
If you find any async Client Component in your code, remove the async keyword and start the development server again on another port with next dev -p 1234.
Should You Use Next.js 13?
If you already are using Next.js 12 and are thinking if you should migrate to version 13, I would say, do it. Next.js 13 is more or less backward compatible. You can bump to 13 and partly upgrade to use the new features according to your time and requirements. See their migration guide here.
If starting a new project or migrating an existing one to version 13, you should know that some of the features are still in beta. The app folder routing which is discussed in this article is production ready since version 13.4.
If you are not using Next.js at all, try it out. Remember that React itself is still just a library. Next.js is a complete framework based on React which have just made it all better.