Skip to content
DocsErrorsNo async client component

No async client component

Client components cannot be async functions.

Why This Error Occurred

The error occurs when you try to define a client component as an async function. React client components do not support async functions. For example:

'use client'
 
// This will cause an error
async function ClientComponent() {
  // ...
}

Possible Ways to Fix It

  1. Convert to a Server Component: If possible, convert your client component to a server component. This allows you to use async/await directly in your component.
  2. Remove the async keyword: If you need to keep it as a client component, remove the async keyword and handle data fetching differently.
  3. Use React hooks for data fetching: Utilize hooks like useEffect for client-side data fetching, or use third-party libraries.
  4. Leverage the use hook with a Context Provider: Pass promises to child components using context, then resolve them with the use hook.

We recommend fetching data on the server. For example:

app/page.tsx
export default async function Page() {
  let data = await fetch('https://api.vercel.app/blog')
  let posts = await data.json()
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

Using use with Context Provider

Another pattern to explore is using the React use hook with a Context Provider. This allows you to pass Promises to child components and resolve them using the use hook . Here's an example:

First, let's create a separate file for the context provider:

app/context.tsx
'use client'
 
import { createContext, useContext } from 'react'
 
export const BlogContext = createContext<Promise<any> | null>(null)
 
export function BlogProvider({
  children,
  blogPromise,
}: {
  children: React.ReactNode
  blogPromise: Promise<any>
}) {
  return (
    <BlogContext.Provider value={blogPromise}>{children}</BlogContext.Provider>
  )
}
 
export function useBlogContext() {
  let context = useContext(BlogContext)
  if (!context) {
    throw new Error('useBlogContext must be used within a BlogProvider')
  }
  return context
}

Now, let's create the Promise in a Server Component and stream it to the client:

app/page.tsx
import { BlogProvider } from './context'
 
export default function Page() {
  let blogPromise = fetch('https://api.vercel.app/blog').then((res) =>
    res.json()
  )
 
  return (
    <BlogProvider blogPromise={blogPromise}>
      <BlogPosts />
    </BlogProvider>
  )
}

Here is the blog posts component:

app/blog-posts.tsx
'use client'
 
import { use } from 'react'
import { useBlogContext } from './context'
 
export function BlogPosts() {
  let blogPromise = useBlogContext()
  let posts = use(blogPromise)
 
  return <div>{posts.length} blog posts</div>
}

This pattern allows you to start data fetching early and pass the Promise down to child components, which can then use the use hook to access the data when it's ready.

Client-side data fetching

In scenarios where client fetching is needed, you can call fetch in useEffect (not recommended), or lean on popular React libraries in the community (such as SWR or React Query) for client fetching.

app/page.tsx
'use client'
 
import { useState, useEffect } from 'react'
 
export function Posts() {
  let [posts, setPosts] = useState(null)
 
  useEffect(() => {
    async function fetchPosts() {
      let res = await fetch('https://api.vercel.app/blog')
      let data = await res.json()
      setPosts(data)
    }
    fetchPosts()
  }, [])
 
  if (!posts) return <div>Loading...</div>
 
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}