Payload with NextJs and SSG

default discord avatar
Chris_Heinz
last month
66

I have recently experimented a lot with a side project build on top of this template

https://github.com/payloadcms/nextjs-custom-server

and I am really blown away by payload and I am looking forward to next weeks launch week. In order to improve SEO I want to migrate the rendering method to SSG (static) but I am having trouble to do so or finding resources / examples. So I wanted to check with the community if you know any public code bases or using SSG by yourself and if someone can give me a example how to use dynamic pages with SSG. Next step would be to set up something like IRS for rebuilding changed parts or just a a hook for triggering a whole rebuild from the admins pages. So similar solutions are also more than welcome.

  • default discord avatar
    Marťafiixek
    last month

    "In order to improve SEO", isn't the template already server rendered?

  • default discord avatar
    Alessio 🍣
    last month

    check this:

    https://nextjs.org/docs/basic-features/data-fetching/get-static-props

    SSG with Next.JS would simply mean that you run the

    fetch

    which fetches data from PayloadCMS in the getStaticProps function. It's pretty independent from the CMS you use, actually!



    As for SSR, the repo you linked already does SSR:

    https://github.com/payloadcms/nextjs-custom-server/blob/master/pages/%5B...slug%5D.tsx


    getStaticProps => SSG



    getServerSideProps => SSR

  • default discord avatar
    Chris_Heinz
    last month

    Yeah that is the reason I wanna switch to SSG

  • default discord avatar
    Marťafiixek
    last month

    How do you want to improve SEO when switching to SSG when SSR and SSG have same impact on SEO?

  • default discord avatar
    Alessio 🍣
    last month

    Prob pagespeed



    that's the additional extra Impact it has

  • default discord avatar
    Marťafiixek
    last month

    Fucking hell, I can't write even one sentence correctly today

  • default discord avatar
    Chris_Heinz
    last month

    I am sorry for the confusion I wrote SSR once when I wanted to write SSG how are those almost the same abbreviations lol

  • default discord avatar
    Marťafiixek
    last month

    So you want to move to SSG because of performance / page speed?

  • default discord avatar
    Chris_Heinz
    last month

    My main concern for the switch is that the source code is predefined for SSR which is arguably better for SEO and my use case is mainly landing pages witch mostly static content



    Thanks @Alessio 🍣 for the links I also checked the next docs and tried to do the switch but I ran into problem witch getStaticPaths especially because of different locales. I will give it another try tomorrow and I recognized that this repo is also using IRS maybe I will start from there

    https://github.com/payloadcms/website-cms
  • default discord avatar
    Alessio 🍣
    last month

    Different locales should totally be possible with getstaticpaths+props, so just SSG - I did something similar once



    You just need to return all paths with the correct locales in getstaticpaths



    I always did my own i18n though, instead of using nextjs' i18n. More customizable that way

  • default discord avatar
    Marťafiixek
    last month

    Well, you have:



    SSR - Server side rendering


    SSG - Static site generation



    SSR and SSG both have same impact on SEO (if we don't count in page speed). They have same impact because when you request the HTML, every possible content on that page is already rendered thus the search engine that crawls your page sees everything correctly.



    The

    nextjs-custom-server

    repository is server rendered. If you wish to use SSG, I would suggest to not use that repo as a template and completely split your CMS from the frontend part.



    Why?



    Because using SSG with that

    nextjs-custom-server

    repo isn't possible. When you want to build the Next.js application using SSG, you need to have your CMS already running, which isn't possible with that repo, because you are building both things at the same time.



    That's also why this is written in the README



    Either that or I didn't way a nice how to use SSG with that repo



    But I generally think that it's better to completely split CMS and FE

    chrome_sPQpIvhCsX.png
  • default discord avatar
    Alessio 🍣
    last month

    Can be nice having it running on the same server for SSR/ISR parts, though - less latency between client and server!

  • default discord avatar
    Marťafiixek
    last month

    I mean yes, but I still have my FE part and CMS running on the same server, just split to two Docker containers

  • default discord avatar
    Chris_Heinz
    last month

    Thanks for the clarification tbh I had a misconception of the impact of SSG as I thought there was a bigger difference between both. On

    https://nextjs.org/learn/seo/rendering-and-ranking/rendering-strategies

    they also write „Static site generation is probably the best type of rendering strategy for SEO“ but then they further explain that with an increase of speed. You always learn something new. So I need to reconsider the migration. I also thought about splitting but I liked the idea to keep both at the same server because you dont have to host two instances for each project but does is make a difference in speed to keep it seperated tho?

  • default discord avatar
    Alessio 🍣
    last month

    mh that's fair! That makes me wonder though: is it possible to use payload's local API in that nextjs-custom-server example - from nextjs?

  • default discord avatar
    Marťafiixek
    last month

    Yes, it's possible, you can use local API inside

    getServerSideProps

    , which is a big plus

  • default discord avatar
    Alessio 🍣
    last month

    Speed is certainly important, but not as important as people make it seem. As long as your speed isn't

    bad

    , it's just used as a tie-breaker by google



    very nice - does that work with your solution - using 2 Docker containers - as well, though?

  • default discord avatar
    Marťafiixek
    last month

    Hah, no 😦 I need to do standard REST API or GraphQL fetches, which is a minus, a bit

  • default discord avatar
    Alessio 🍣
    last month

    ah too bad. I'm personally thinking of migrating my project to just one single express server just for that sweet local API.



    Gotta wait for proper edge hosting support though!

  • default discord avatar
    Chris_Heinz
    last month

    So it seems like SSG or IRS brings no big advantage over SSR so I probably stick with that for now.

  • default discord avatar
    Marťafiixek
    last month

    You can think of it this way:



    - When you have both Next.js and Payload running on one Express server, you will have just one server to maintain, typically some VPS, which however means, that you will probably use SSR and you will have to push your assets to CDNs like Cloudflare yourself



    - When you have only Payload on that VPS and Next.js as separate application, you can host Next.js on Vercel or Netlify, which means, that you will probably use SSG, which can speed up your application to end users, as Vercel and Netlify both push your static assets to their CDNs

  • default discord avatar
    Chris_Heinz
    last month

    Super helpful I was also thinking about a proper file storage solution.

  • default discord avatar
    Marťafiixek
    last month

    With SSG?

  • default discord avatar
    Alessio 🍣
    last month

    mh yeah, or at least ISR. There must be a way to get SSG to work as well



    Never thought about your point "When you want to build the Next.js application using SSG, you need to have your CMS already running, which isn't possible with that repo, because you are building both things at the same time. ". Sounds tricky when I think about it

  • default discord avatar
    Marťafiixek
    last month

    I'm gonna pull the example / template repo again and try something



    Saw something when looking at the source code

  • default discord avatar
    Chris_Heinz
    last month

    Feel free to share your findings here 🙂

  • default discord avatar
    Marťafiixek
    last month

    So, SSG is definitely possible with that template, one minor thing is that I can't seem to get local API to work, even though local API works with SSR



    I saw in the code that Next.js builds after the Express app is started, which is what we want, therefore any

    fetch(localhost:3000/api/pages)

    I do before the build of Next returns correct data as it should



    But I'm like really tired so maybe what I'm saying doesn't even make sense, gonna take a look at it tomorrow

  • default discord avatar
    Alessio 🍣
    last month
    that Next.js builds after the Express app is started

    Could you link that code?



    oh nvm found it. Interesting



    veery interesting. Didn't know next can do the building outside of npm run build. I wonder if Sveltekit has something like that too

  • default discord avatar
    Marťafiixek
    last month

    Basically (see comments in code):



    const start = async () => {
      await payload.init({
        secret: process.env.PAYLOAD_SECRET_KEY,
        mongoURL: process.env.MONGO_URL,
        express: server,
      });
    
      if (!process.env.NEXT_BUILD) {
        // Builds Next before Express starts, Payload is not accessible at this moment
        // Probably used when running locally in dev mode eg. yarn dev
        const nextApp = next({ dev });
    
        const nextHandler = nextApp.getRequestHandler();
    
        server.get('*', (req, res) => nextHandler(req, res));
    
        nextApp.prepare().then(() => {
          console.log('NextJS started');
    
          server.listen(process.env.PORT, async () => {
            console.log(`Server listening on ${process.env.PORT}...`);
          });
        });
      } else {
        // Builds Next after Express starts, Payload is accessible at this moment
        server.listen(process.env.PORT, async () => {
          console.log('NextJS is now building...');
          await nextBuild(path.join(__dirname, '../'));
          process.exit();
        });
      }
    };
  • default discord avatar
    Alessio 🍣
    last month

    this doesn't do SSR then though, does it?



    So just pure SSG with 0 SSR

  • default discord avatar
    Marťafiixek
    last month

    It depends if you have getServerSideProps or getStaticProps in your Next.js code



    If you do REST API fetch in the

    else

    condition to

    localhost:3000/api/pages

    , you get data correctly, therefore you can access Payload itself



    Then, when I had

    payload.find

    eg. local API inside

    getServerSideProps

    , and I accessed that page in browser, everything worked fine



    However, the moment I only changed

    getServerSideProps

    to

    getStaticProps

    I got

    APIError: The collection with slug pages can't be found.


    The Express app only returns the prerendered HTML Next.js did generate when building, therefore it's basically SSG, unless you use

    getServerSideProps

    in Next code, then usual server side stuff happens

  • default discord avatar
    Alessio 🍣
    last month
    unless you use getServerSideProps

    getServerSideProps still runs even though nextApp.prepare() or server.get('*', (req, res) => nextHandler(req, res));


    isn't run?



    Like in the else, it just exits the process - doesn't seem to start any next server



    And yeah, weird you get that error in getStaticProps



    maybe it runs too early?

  • default discord avatar
    Marťafiixek
    last month

    I mean, mainly the original idea was a bit different than what I'm trying to do. Because the

    build

    script from

    package.json

    literally does this:



    "build": "cross-env NODE_ENV=production yarn build:payload && yarn build:server && yarn copyfiles:media && yarn build:next && yarn serve",

    It builds Payload, builds Express, builds Next (where it would fail miserably if we had some

    getStaticProps

    inside your Next code) and only then it does

    yarn serve

    which runs everything at once



    Therefore if you wanted to use SSG (for demonstration purposes without local API, and only with REST API from Next.js), you would have to modify the above to


    const start = async () => {
      await payload.init({
        secret: process.env.PAYLOAD_SECRET_KEY,
        mongoURL: process.env.MONGO_URL,
        express: server,
      });
    
     server.listen(process.env.PORT, async () => {
       console.log('NextJS is now building...');
       await nextBuild(path.join(__dirname, '../'));
     });
    };


    And you would have to modify the build script to something in terms of:


    - Build Payload


    - Build server


    - Run server and after server is online start building Next.js



    Lemme try it



    That's why I was stating to @Chris_Heinz that with the template, SSG is a bit hard without any modifications haha 😄



    @Alessio 🍣 So, I just fiddled bit with what I wrote, and no, sadly you can't use Local API and SSG together



    You can however use that template we are talking about and use SSG there with a few modifications, still you are going to lose the ability to use Local API



    It's simple why you can't, see the comments in the code:


    // This starts the Express server
    server.listen(process.env.PORT, async () => {
    
      // This builds the Next.js app
      await nextBuild(path.join(__dirname, "../"));
      
      // This launches the Next.js app
      const nextApp = next();
    
      // This initializes the route handler Next.js uses
      const nextHandler = nextApp.getRequestHandler();
    
    
      // Any request made to the Express server is passed to Next.js
      // This is crucial, as request and response is sent to Next.js
      // Response from the Express server contains the Payload object
      // The Payload object is needed to be able to use Local API
      // However, you need to build the Next.js app before any of this
      // This means that Next.js build process will never have access to the correct Payload object
      server.get("*", (req, res) => nextHandler(req, res));
    
    
      // Next.js is launched from here
      nextApp.prepare().then(() => {
        console.log("Next.js ready");
      });
    });


    That's why you can use it with SSR, and you can't with SSG



    I just wanted to clarify (as I saw that this thread got traction again), that the above is still valid, however, I think it should be also possible to use Local API inside SSG in Next.js during build time.



    You would need to create the Payload object (because you can't get it from request) inside your Next.js application during that build time. Similarly how is it used inside scripts:


    await payload.init({
        secret: PAYLOAD_SECRET,
        mongoURL: MONGODB_URI,
        local: true, // Enables local mode, doesn't spin up a server or frontend
      });
  • default discord avatar
    Chris_Heinz
    2 weeks ago

    Do you have an idea how to check in the build script whether the server is up and responding ?

  • default discord avatar
    noheadphones
    2 weeks ago

    I think you could set this up in a re-usable hook, just gotta be careful to not leak it into the client side, I'll play around with it when I have the time, especially now that

    app

    is out stable in nextjs

  • default discord avatar
    Chris_Heinz
    2 weeks ago

    Just if anyone else runs into that and want to achieve something similar I post the code to make SSG work with the next-custom-server


      // npm script "build:next": "cross-env NEXT_BUILD=true PAYLOAD_CONFIG_PATH=dist/payload.config.js node dist/server.js",
    
      if (process.env.NEXT_BUILD) {
          const waitOn = require("wait-on");
          const options = {
            // test some api route 
            resources: [`${process.env.SERVER_URL}/api/globals/header`],
          };
    
          server.listen(process.env.PORT, async () => {
            console.time("wait time");
            await waitOn(options);
            // this is optional for checking who long it takes for the server to be up and running
            console.timeEnd("wait time");
            console.log("NextJS is now building...");
            await nextBuild(path.join(__dirname, "../"));
            process.exit();
          });
        }
      }
  • default discord avatar
    jamestucker
    2 weeks ago

    So I am looking at making a personal website using the template (and to learn payloadcms for a future projects). To clarify, this will mean I can use nextjs-custom-server to build the app and I am able to use SSG on it using this? Or am I better off creating a separate next.js repository to serve the content? Cheers

  • default discord avatar
    Marťafiixek
    2 weeks ago

    Yes, that's exactly it



    On the other hand I think it's still better in some cases to separate FE from CMS

Open the post
Continue the discussion in Discord
Like what we're doing?
Star us on GitHub!

Star

Connect with the Payload Community on Discord

Discord

online

Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.