reactjsnext.jsnext.js13nextjs14

Dynamic server usage: Page couldn't be rendered statically because it used `headers`


CODE

Page component:

import React from 'react';
import PricingPlansMain from '@/components/pages/PricingPlansPage';
import { getMeAction } from '@/server/auth-actions';
import { getAgentPricingPlansAction } from '@/server/payment-actions';
import PageLayout from '@/components/ui/PageLayout';
import UserCtxLayoutProvider from '@/components/ui/UserCtxLayoutProvider';
import styles from '@/components/pages/PricingPlansPage/styles.module.css';

export default async function PricingPlansPage() {
  const userData = (await getMeAction())?.success;
  const plans = await getAgentPricingPlansAction();

  return (
    <UserCtxLayoutProvider userData={userData}>
      <PageLayout
        headerClass={styles.header}
        headerTheme='light'
        logoConfig={{ theme: 'black', variant: 'full' }}
        headerVersion='full'
        footerTheme='default'
      >
        <PricingPlansMain plans={plans} />
      </PageLayout>
    </UserCtxLayoutProvider>
  );
}

Actions:

export const getMeAction = async () => {
  try {
    const res = await fetch(`${process.env.BACKEND_BASE}/users/me`, {
      cache: 'no-store',
      headers: { ...getAuthorizationHeader() },
      next: { tags: [getCacheKey('userData')] },
    });
    if (!res.ok) {
      throw new Error('server_error');
    }
    const { data } = await res.json();
    return { success: data as UserType };
  } catch (err) {
    return { error: 'server_error' };
  }
};
export async function getAgentPricingPlansAction() {
  try {
    const res: Response = await fetch(
      `${process.env.BACKEND_BASE}/payments/plans/prices`,
      {
        method: REQUEST_METHODS.GET,
        headers: {
          ...getNodeRequiredRequestHeaders(),
          ...getAuthorizationHeader(),
        },
      }
    );
    if (!res.ok) {
      throw new Error('unexpected error');
    }
    const data: PricingPlansActionResponseType = await res.json();
    return data;
  } catch (err: any) {
    throw new Error(err.message as string);
  }
}

I'm using the App Router in my Next.js project and running into the following error when trying to pre-render a page:

Error: Dynamic server usage: Page couldn't be rendered statically because it used `headers`. See more info here: https://nextjs.org/docs/messages/dynamic-server-error
    at o (D:\upwork\movein\new frontend\muuve-frontend\.next\server\app\(website)\pricing-plans\page.js:1:13234)
    at o (D:\upwork\movein\new frontend\muuve-frontend\.next\server\app\(website)\pricing-plans\page.js:1:12434)
Error occurred prerendering page "/pricing-plans". Read more: https://nextjs.org/docs/messages/prerender-error

The error occurs when I call a function to fetch pricing plans from the server. However, if I comment out the part of the code that fetches the pricing plans, everything works fine.


Solution

  • I have solved this error by using this workarround: isDynamicServerError. i have copied the solution from this post : Dynamic server usage: Page couldn't be rendered statically because it used `nextUrl.searchParams` in Next.js version 14

    export const getMeAction = async () => {
      try {
        const res = await fetch(`${process.env.BACKEND_BASE}/users/me`, {
          cache: 'no-store',
          headers: { ...getAuthorizationHeader() },
          next: { tags: [getCacheKey('userData')] },
        });
        if (!res.ok) {
          throw new Error('server_error');
        }
        const { data } = await res.json();
        return { success: data as UserType };
      } catch (err) {
        /** NOTE
         * This is a workaround
         * For the issue of next.js not being able to render the page statically
         * This issue arises in the pricing page
         * https://stackoverflow.com/questions/78010331/dynamic-server-usage-page-couldnt-be-rendered-statically-because-it-used-next
         **/
        if (isDynamicServerError(err)) {
          throw err;
        }
        return { error: 'server_error' };
      }
    };
    

    But again my question why this error is aresing only in this page becase i have other pages too where i am calling the same server action and i am not getting this error there

    import DashboardProperitesMain from '@/components/pages/DashboardProperitesPage';
    import UserCtxLayoutProvider from '@/components/ui/UserCtxLayoutProvider';
    import { PropertiesStatusFilterEnum } from '@/config';
    import { Roles } from '@/schemas';
    import { getMeAction } from '@/server/auth-actions';
    import { redirectBaseOnRole } from '@/server/helper';
    import { UserType } from '@/types';
    
    export default async function DashboardHome({
      searchParams,
    }: {
      searchParams?: {
        page?: string;
        status?: PropertiesStatusFilterEnum;
      };
    }) {
      const currentPage = Number(searchParams?.page) || 1;
      const userData = (await getMeAction())?.success;
      redirectBaseOnRole(userData, [
        Roles.ADMIN,
        Roles.LANDLORD,
        Roles.SUB_USER,
        Roles.AGENT,
      ]);
      return (
        <UserCtxLayoutProvider userData={userData}>
          <DashboardProperitesMain
            userData={userData as UserType}
            currentPage={currentPage}
            status={searchParams?.status}
          />
        </UserCtxLayoutProvider>
      );
    }
    
    

    Main:

    import React from 'react';
    
    import { Suspense } from 'react';
    import TableWrapper from '@/components/pages/DashboardProperitesPage/TableWrapper';
    import Header from '@/components/pages/DashboardProperitesPage/Header';
    import { DashboardProperitesTableSkelton } from '@/components/ui/DashboardPropertiesTable';
    import { UserType } from '@/types';
    import { PropertiesStatusFilterEnum } from '@/config';
    type Props = {
      currentPage: number;
      userData: UserType | undefined;
      status?: PropertiesStatusFilterEnum;
    };
    export default function DashboardProperitesMain({
      currentPage,
      userData,
      status,
    }: Props) {
      return (
        <section className=' max-w-[1300px] mx-auto '>
          <Header />
          <Suspense
            key={`${currentPage}-${status}`}
            fallback={<DashboardProperitesTableSkelton />}
          >
            <TableWrapper
              status={status}
              userData={userData}
              currentPage={currentPage}
            />
          </Suspense>
        </section>
      );
    }
    
    

    Table Wrapper:

    import React from 'react';
    import {
      getAllProperitesAction,
      getMyProperitesAction,
    } from '@/server/property-actions';
    import { DashboardPropertiesTable } from '@/components/ui/DashboardPropertiesTable';
    import { UserType } from '@/types';
    import { Roles } from '@/schemas';
    import { PropertiesStatusFilterEnum } from '@/config';
    type Props = {
      currentPage: number;
      userData: UserType | undefined;
      status?: PropertiesStatusFilterEnum;
    };
    export default async function TableWrapper({
      currentPage,
      userData,
      status,
    }: Props) {
      let properitesData;
    
      if (userData?.role === Roles.ADMIN) {
        properitesData = await getAllProperitesAction(
          currentPage,
          status
        );
      } else {
        properitesData = await getMyProperitesAction(currentPage, status);
      }
    
      return (
        <DashboardPropertiesTable
          data={properitesData.data}
          totalDocs={properitesData.totalDocs}
          currentPage={currentPage}
        />
      );
    }