Search code examples
reactjstypescriptnext.jssupabase

Error: "Only async functions are allowed to be exported in a 'use server' file"


I am trying to use layout.tsx in the app directory of Next.js 13 to have a nav layout that is present on all the pages. I managed to make it so that when the user logs out/signs in, the navbar changes, but the issue is that I have to refresh the page (F5) to see the change on the navbar. I think this issue is related to the cache, and that's why I am trying to use export const dynamic = 'force-dynamic'.

I also added the client component to a server component because I thought that would be the issue, but it didn't solve the problem. I wanted to use export const dynamic = 'force-dynamic' to deal with the cache, but now I'm encountering an error that I can't seem to resolve. The error message I'm getting is:

Error

Only async functions are allowed to be exported in a "use server" file.

And here is the detailed error trace:

./app/components/ClientInsideServerLayout.tsx
Error: 
  x Only async functions are allowed to be exported in a "use server" file.
   ,-[C:\Users\zantl\OneDrive\Documentos\GitHub\sssss\gestion-gastos-supabase\app\components\ClientInsideServerLayout.tsx:2:1]
 2 | 
 3 | import { getSessionStatus } from "../ServerActions/isUserLoggedIn";
 4 | import ClientLayout from "./ClientLayout"
 5 | export const dynamic = 'force-dynamic'
   : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 6 | 
 7 | export async function ClientInsideServerLayout() {
 7 |     const isLoggedIn = await getSessionStatus();
   `----

Code

Here's the code for each relevant file that's causing the error:

File: ClientInsideServerLayout.tsx

'use server';

import { getSessionStatus } from "../ServerActions/isUserLoggedIn";
import ClientLayout from "./ClientLayout"
export const dynamic = 'force-dynamic'

export async function ClientInsideServerLayout() {
    const isLoggedIn = await getSessionStatus();
    return (
      <>
      <ClientLayout isLoggedIn={isLoggedIn}></ClientLayout>
      </>
    )
  }

File: ClientLayout.tsx

'use client'

import Link from 'next/link';
import { useRouter } from 'next/navigation'

export default function ClientLayout({ isLoggedIn }: { isLoggedIn: boolean }) {
  const router = useRouter();

  const signOut = async () => {
    try {
      const response = await fetch("http://localhost:3000/auth/sign-out", {
        method: "POST"
      });
  
      if (response.ok) {
        router.push("/")
        console.log('The resource has been permanently moved.');
      }     
    } catch (error: unknown) {
      console.error('An error occurred:', error);
    }
  };
  
  const logIn = () =>{
    router.push("/login")
  }
  
  
  
  
  return (
    <nav className="flex items-center justify-between flex-wrap bg-teal-500 p-6">
      <div className="flex items-center flex-shrink-0 text-white mr-6">
        {/* ... logo code ... */}
      </div>
      <div className="block lg:hidden">
        {/* ... button code ... */}
      </div>
      <div className="w-full block flex-grow lg:flex lg:items-center lg:w-auto">
        <div className="text-sm lg:flex-grow">
          <Link href="/ingresos" passHref>
            <span className="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white">
              Ingresos
            </span>
          </Link>
        </div>
        <div>
          {isLoggedIn ? (
            <button onClick={signOut} className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0">Log Out</button>
          ) : (
            <>
              <button onClick={logIn} className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0 mr-2">Log In</button>
              <button className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0">Sign Up</button>
            </>
          )}
        </div>
      </div>
    </nav>
  );
}

Here's the code for the RootLayout that I'm using:

// Existing RootLayout code
'use server';
import './globals.css';
import { getSessionStatus } from './ServerActions/isUserLoggedIn';
import { ClientInsideServerLayout } from './components/ClientInsideServerLayout';

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const isLoggedIn = await getSessionStatus();

  return (
    <html lang="en">
      <body>
        <div>
          <ClientInsideServerLayout></ClientInsideServerLayout>
          {children}
        </div>
      </body>
    </html>
  );
}

Here's the directory structure for context:

app/
  auth/
    callback/
      route.ts
    sign-in/
      route.ts
    sign-out/
      route.ts
    sign-up/
      route.ts
  components/
    ClientInsideServerLayout.tsx
    ClientLayout.tsx
    Login.tsx
    tablaIngresos.tsx
  ingresos/
    page.tsx
  login/
    messages.tsx
    page.tsx
  ServerActions/
    isUserLoggedIn.ts
  _examples/
    client-component/
      page.tsx
    route-handler/
      route.ts
    server-action/
      page.tsx
    server-component/
      page.tsx
  auth-form.tsx
  favicon.ico
  globals.css
  layout.tsx
  page.tsx
components/
  LogoutButton.tsx
  NextJsLogo.tsx
  SupabaseLogo.tsx
types/
  supabase.ts
.env.local
.gitignore
middleware.ts
next-env.d.ts
next.config.js
package-lock.json
package.json
postcss.config.js
README.md
tailwind.config.js
tsconfig.json

Question

Can someone explain why this error is happening, and how can I fix it while keeping the layout update functionality when clicking the log-out button? I want to achieve this without having to refresh the page.

Any insights or suggestions would be greatly appreciated! Thank you!


Solution

  • I had a similar issue in next 14 where I was using an index.ts file to centralize my exports of server actions. The issue was that I needed to remove the 'use server' from the index.ts file itself.

    WRONG:

    'use server'
    
    /**
     * Simplify and centralize server action exports
     */
    export { signIn } from "./signIn";
    export { signOut } from "./signOut";
    export { createComment } from "./createComment";
    export { createPost } from "./createPost";
    export { createTopic } from "./createTopic";
    
    

    CORRECT: Remove use server from top of file.

    /**
     * Simplify and centralize server action exports
     */
    export { signIn } from "./signIn";
    export { signOut } from "./signOut";
    export { createComment } from "./createComment";
    export { createPost } from "./createPost";
    export { createTopic } from "./createTopic";
    
    

    Example of createComment server action

    "use server";
    
    export async function createComment() {}
    
    

    Properly exporting an async function from it with use server at the top of each individual server action file.

    Hope this helps someone.