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!
export const dynamic = 'force-dynamic
is not an async function...plaintext x Server actions must be async functions
This arises because server actions in Next.js 13 must be async. The lineexport const dynamic = 'force-dynamic'
is intended to address caching, but conflicts with these requirements. I need to find another way to comply with Next.js 13's rules. Any further insights would be welcomed!