I have recently experimented a lot with a side project build on top of this template
https://github.com/payloadcms/nextjs-custom-serverand 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.
"In order to improve SEO", isn't the template already server rendered?
check this:
https://nextjs.org/docs/basic-features/data-fetching/get-static-propsSSG 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
Yeah that is the reason I wanna switch to SSG
How do you want to improve SEO when switching to SSG when SSR and SSG have same impact on SEO?
Prob pagespeed
that's the additional extra Impact it has
Fucking hell, I can't write even one sentence correctly today
I am sorry for the confusion I wrote SSR once when I wanted to write SSG how are those almost the same abbreviations lol
So you want to move to SSG because of performance / page speed?
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 @alessiogr 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-cmsDifferent 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
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
Can be nice having it running on the same server for SSR/ISR parts, though - less latency between client and server!
I mean yes, but I still have my FE part and CMS running on the same server, just split to two Docker containers
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-strategiesthey 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?
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?
Yes, it's possible, you can use local API inside
getServerSideProps
, which is a big plus
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?
Hah, no 😦 I need to do standard REST API or GraphQL fetches, which is a minus, a bit
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!
So it seems like SSG or IRS brings no big advantage over SSR so I probably stick with that for now.
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
Super helpful I was also thinking about a proper file storage solution.
With SSG?
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
I'm gonna pull the example / template repo again and try something
Saw something when looking at the source code
Feel free to share your findings here 🙂
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
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
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();
});
}
};
this doesn't do SSR then though, does it?
So just pure SSG with 0 SSR
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
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?
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 😄
@alessiogr 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
});
Do you have an idea how to check in the build script whether the server is up and responding ?
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
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();
});
}
}
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
Yes, that's exactly it
On the other hand I think it's still better in some cases to separate FE from CMS
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.