Thoughts on software development, technology, and other random philosophical matters.
Daryl Wright /     (3 mins)

While converting this website to Next.js from Gatsby, I had to translate my approaches from the latter to the former. Specifically, how to render individual components on the server. As Gatsby sites are static by default, this was not something I thought much about. Working with Next.js and TypeScript had me scratching my head on this issue. I couldn't figure out how to render async components in TypeScript without being met with the following error:

TS2786: 'SomeAsyncComponent' cannot be used as a JSX component. Its return type
'Promise<ReactElement<any, string | JSXElementConstructor<any>>>' is not a valid
JSX element. Type 'Promise<ReactElement<any, string | JSXElementConstructor<any>>>'
is missing the following properties from type 'ReactElement<any, any>': type, props, key

Without being able to leverage async components, I was forced to do async processing/rendering on the client with use() or useEffect() hooks. This was not an ideal solution, and it also has some limitations. For instance, if I wanted to call an external API inside a component, I would run into CORS issues, unless I had access to the API server, at least to the capacity where I could add a setting to allow the domain of my application.

Frustrated with this state of affairs, I did a search on how to use async components in a Next.js application. This search didn't return any useful results. From what I could see, using async components was supposed to 'just work', but I couldn't understand why it didn't work for me. That was until I discovered this GitHub issue, specifically, this comment.

To summarize, it appears that the TypeScript compiler does not yet have support for async JSX components. This is why I was unable to even build my application, rather than just experiencing error output in the client. The following directive placed above the async component solves this problem.

{/* @ts-expect-error Server Component */}
<MyAsyncComponent />

@ts-expect-error alone will suffice, but adding the Server Component comment adds helpful context as to why an error is expected. When TypeScript eventually supports async JSX components (or if this is accomplished via a Next.js plugin), the @ts-expect-error directive will throw an error due to no error being thrown by the component. You can then remove these directives and freely use async JSX components in your code.

Daryl Wright's picture
About Daryl Wright

Daryl is an experienced technology advisor who specializes in creating innovative software solutions and solving novel problems. He has spent over a decade providing technical expertise in various industries and is well versed in a wide variety of programming languages, tools, and frameworks. Daryl is the owner of Golden Path Technologies, a proud father, husband, and dog parent.

22 posts Miramichi, New Brunswick, Canada https://goldenpath.ca Github Reddit Mastodon Bluesky