skip to Main Content

I am using nextjs v15. I have some pages where my content is fully static, and as such I want to pre-render it. However, in my layout.tsx, each page also has my header component which is not static, and retrieves auth data that has headers/cookies.

If I attempt to export dynamic to ‘error’ as such:

export dynamic = 'error';

which will try to force the page to be rendered statically, and will error out if it can’t, then I get this:

Error: Route / with `dynamic = "error"` couldn't be rendered statically because it used `headers`. See more info here: 
https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering

and then some reference code to my layout.tsx where I include the auth() call.

Is there a way to prerender statically the actual page, but not the header? Any other alternatives?

Instead of getting the authentication on the server, I could just have my header / client component do the auth call, but then my header has a loading state waiting for the authentication. I don’t want that, and just want my header/authentication to be fully rendered by the time it is sent to the user.

2

Answers


  1. You have several options:

    • Keep your page statically rendered and do dynamic logic / hydration on the client side via hooks e.g. useEffect.
    • Move to Server Components. Have your skeleton as Server Component and your header content as Client Component. Server Component rendering result can be optionally cached on the server for optimization.
    • Stop enforcing static generation. Move to SSR.
    Login or Signup to reply.
  2. To make your page static, you should not be using a Dynamic API and cache your data

    rendering

    An example

    interface Post {
      id: string
      title: string
      content: string
    }
    
    export default async function Page() {
        const data = await fetch('https://api.vercel.app/blog', { cache: 'force-cache' })
        const posts: Post[] = await data.json()
    
        return (
            <main>
                <h1>Blog Posts</h1>
                <ul>
                    {posts.map((post) => (
                        <li key={post.id}>{post.title}</li>
                    ))}
                </ul>
            </main>
        )
    }
    

    If you want your header to be visible without waiting your auth call to finish, consider Streaming

    Streaming enables you to progressively render UI from the server. Work is split into chunks and streamed to the client as it becomes ready. This allows the user to see parts of the page immediately, before the entire content has finished rendering.

    To do that, define your Header component as a client component and pass a server component in which you do your auth call as a prop to it (supported pattern)

    Your component in which you do your auth call

    "use server"
    
    function wait(milliseconds) {
        return new Promise(resolve => setTimeout(resolve, milliseconds));
    }
    
    const SC = async () => {
        await wait(3000) // to show a progressive loading in your header, replace with auth call
        
        return (
            <div style={{ background: "green", width: "100%", height: "50px" }}>
                Dynamic Part of the Header
            </div>
        )
    }
    
    export default SC
    

    Your header component

    "use client"
    
    import { Suspense } from "react"
    
    export default function Header({ children }: { children: React.ReactNode }) {
        return (
            <div>
                <div style={{ background: "red", width: "100%", height: "20px" }}>
                    Static Part of the Header
                </div>
                <Suspense>
                    {children}
                </Suspense>
            </div>
        )
    }
    

    Your layout

    import type { Metadata } from "next"
    import Header from "./ui/Header"
    import SC from "./ui/SC"
    import "./globals.css"
    
    export const metadata: Metadata = {
        title: "Create Next App",
        description: "Generated by create next app",
    }
    
    export default function RootLayout({
        children,
    }: Readonly<{
        children: React.ReactNode;
    }>) {
      return (
            <html lang="en">
                <body>
                    <Header>
                        <SC />
                    </Header>
                    {children}
                </body>
            </html>
        )
    }
    

    Application starts and…
    enter image description here

    after 3 seconds

    enter image description here

    Read the following links for further information

    https://nextjs.org/docs/app/building-your-application/rendering/server-components#static-rendering-default

    https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming

    https://nextjs.org/docs/app/building-your-application/caching#data-cache

    https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search