9

I am trying to understand why Next.js is building some of my pages as SSG and some of them as Static, when they all are using getStaticProps.

Let's take my 404 page that uses getStaticProps to fetch data from prismic with graphql. It is being rendered as a Static website when in my opinion it should be rendered as SSG (because it uses getStaticProps).

I am doing the EXACT same thing in my 500 page, but with a different graphql query and it is being rendered (in my opinion correctly) as SSG.

Why is that?

404 page:

const NotFound = ({ data: { page } }) => {
    return (
        <div className={'not-found'}>
            <p className={'not-found__description'}>{RichText.asText(page.description)}</p>
        </div>
    );
};

export const getStaticProps = async (context) => {
    const currentLanguage = getCurrentLocale(context);

    const response = await apolloClient.query({
        query: gql`
            query {
            }
        `
    };

    return {
        props: {
            data: {
                page: response
            }
        }
    }
});

export default NotFound;

500 page:

const InternalServerError = ({ data: { page } }) => {
    return (
        <div className={'internal-server-error'}>
             <p className={'internal-server-error__description'}>{RichText.asText(page.description)}</p>
        </div>
    );
};

export const getStaticProps = async (context) => {
    const currentLanguage = getCurrentLocale(context);
    
    const response = await apolloClient.query({
        query: gql`
            query {
            }
        `
    });
    
    return {
        props: {
            data: {
                page: response
            }
        }
    }
};

enter image description here

Damian Kociszewski
  • 283
  • 1
  • 5
  • 20

2 Answers2

4

Current-Deployment-Details

The 404.tsx or 404.js page in Next.js is unique in that it does not rely on the server and is always Static -- relying solely on static html (no json) at build time -- even when using GetStaticProps in the file.

The 404 page is simply a catch all funnel route that users are redirected to when navigating to paths that do not exist with your site as the base URL. So, it doesn't rely on the server on initial build. It's the fallback for paths not existing, and nothing else. The 500 page, on the other hand, handles an internal error in your application so it does rely on both .html and .json file types to pinpoint the nature of the error.

Interestingly, if you examine the contents of your .next directory locally, you'll notice that all pages using GetStaticProps have .json and .html files statically generated. Pages using GetStaticProps with revalidate returned === Incremental Static Regeneration, or ISR. ISR is an ideal hybrid of SSG and SSR, having background functions scanning for incoming changes/updates in production (the number you specify being the amount of time in seconds between possible updates). So, pages with GetStaticProps + ISR generate three file types in the .next directory -- .html, .json, and .js. That said, Pages using GetServerSideProps or GetInitialProps have only .js files generated in the .next directory. Lastly, pages that are purely Static, using none of the aforementioned methods, have only .html files generated.

The idea behind the 404 page and its Static nature is to enhance UX by expediting the rendering (or more correctly prerendering) of a custom oops! that path doesn't exist page so that a user can return to the actual application asap.

For example, I have the following in my 404.tsx page, but it still renders as Static html at build time.

import { Container } from '@/components/UI';
import { initializeApollo, addApolloState } from '@/lib/apollo';
import { NotFound } from '@/components/NotFound';
import { AppLayout } from '@/components/Layout';
import {
    GetStaticPropsContext,
    GetStaticPropsResult,
    InferGetStaticPropsType
} from 'next';
import {
    NotFoundQuery,
    NotFoundDocument,
    NotFoundQueryVariables,
    DynamicNavQuery,
    DynamicNavDocument,
    DynamicNavQueryVariables,
    WordpressMenuNodeIdTypeEnum,
    WordpressMediaItemSizeEnum,
    WordpressPageIdType
} from '@/graphql/generated/graphql';

export function SOS({
    notFound,
    Header,
    Footer
}: InferGetStaticPropsType<typeof getStaticProps>) {
    return (
        <>
            <AppLayout title={'✂ 404 ✂'} Header={Header} Footer={Footer}>
                <Container clean className='fit'>
                    <NotFound notFound={notFound} />
                </Container>
            </AppLayout>
        </>
    );
}

export async function getStaticProps(
    ctx: GetStaticPropsContext
): Promise<
    GetStaticPropsResult<{
        notFound: NotFoundQuery['NotFound'];
        Header: DynamicNavQuery['Header'];
        Footer: DynamicNavQuery['Footer'];
    }>
> {
    const params = ctx.params!;
    console.log(params ?? '');
    const apolloClient = initializeApollo();
    await apolloClient.query<
        DynamicNavQuery,
        DynamicNavQueryVariables
    >({
        query: DynamicNavDocument,
        variables: {
            idHead: 'Header',
            idTypeHead: WordpressMenuNodeIdTypeEnum.NAME,
            idTypeFoot: WordpressMenuNodeIdTypeEnum.NAME,
            idFoot: 'Footer'
        }
    });

    await apolloClient.query<NotFoundQuery, NotFoundQueryVariables>(
        {
            query: NotFoundDocument,
            variables: {
                id: '/404-not-found/' as '/404/',
                idType: WordpressPageIdType.URI,
                size: WordpressMediaItemSizeEnum.LARGE
            }
        }
    );
    return addApolloState(apolloClient, {
        props: {},
        revalidate: 60
    });
}
export default SOS;

Interestingly, because I do use GetStaticProps and revalidate for ISR in my 404.tsx page, the contents of the .next directory reflects this as all three file types are present for 404 (.js, .json, .html). If you use getInitialProps in your custom _app.tsx or _app.js file, then automatic static optimization (the prerendering of static pages) will be disabled across the entirety of your app. Give it a try if you're curious, it should cause the 404 page to have a lambda next to it in your build log. However, since you already have GetStaticProps that should override the app-wide static deoptimization caused by your root app page using GetInitialProps

For example, I used GetInitialProps in _app.tsx prior to creating a custom 404.tsx page some time back. I decided to pull the build logs and took an accompanying screen shot.

Warning: You have opted-out of Automatic Static Optimization due to `getInitialProps` in `pages/_app`. This does not opt-out pages with `getStaticProps`.

404-static-deoptimization

Andrew Ross
  • 1,094
  • 7
  • 16
  • 1
    Ok, but the Next.js docs say 'This file is statically generated at build time. You can use getStaticProps inside this page if you need to fetch data at build time.' (404 & 500 pages). Shouldn't both pages be built the exact same way? – Damian Kociszewski May 07 '21 at 08:50
  • I agree that they should be, and I think they indeed are, if you examine the contents of the `.next` directory you'll see that you have all three file types (js, json, html) when using GetStaticProps + ISR in the 404 page. That said, perhaps the build logs reflect the following: "A 404 page may be accessed very often. Server-rendering an error page for every visit increases the load of the Next.js server. This can result in increased costs and slow experiences." It is static (html only) by default -- adding GetStaticProps overrides that. It's just the symbol remaining unchanged. – Andrew Ross May 08 '21 at 04:13
  • I just spent ~10 minutes sifting through their declaration files to determine how 404 vs 500 is handled under the hood to no avail. Going to ask a question on the nextjs repo about this, you raise a great point. All I can find info on === _error.d.ts with no explicit reference to 404 or 500 error pages. – Andrew Ross May 08 '21 at 04:24
  • 1
    I think you are right. It seems like an issue with Next.js – Damian Kociszewski May 10 '21 at 12:03
0

Is the 404 page missing that parenthesis in your code?

const response = await apolloClient.query({
    query: gql`
        query {
        }
    `
};

should be

const response = await apolloClient.query({
    query: gql`
        query {
        }
    `
});