An example of how this approach can be leveraged is by running a full NextJS site on the same Express app as your Payload CMS instance. We’ve built a boilerplate that demonstrates exactly how this works.
Check it out on GitHub:
This boilerplate includes the following:
When this type of setup is best used
If you know you need a CMS and will be leveraging NextJS in a server-side rendering capacity, and know that you will not be deploying on Vercel, this boilerplate will be perfect for you. This approach can be super valuable and can get you up and running with a full CMS—complete with everything you need to build a modern, blazingly fast site or app including custom validation, full authentication, access control, and much more.
Much of the complexity that we handle within this boilerplate comes from using TypeScript to build a custom NextJS server. At Payload, we’re big fans of TypeScript (all of Payload is written in TS). We’re doing our best to adopt and embrace it completely, and we think that it’s only going to get more and more popular.
This boilerplate contains two
tsconfig.json, which will be used for the entirety of your NextJS app, including all your React components
tsconfig.server.jsonfile, which will handle everything in the
You’ll see that we’ve extended the main
tsconfig.json config within the server config and overridden a few properties.
Due to how NextJS relies on dynamic import statements, it requires that its TypeScript projects specify
"module": "esnext" in their TS configs. But, Express requires the CommonJS pattern — meaning we have no choice but to require two separate TS configs. No big deal, but this is a common “gotcha” when working with NextJS and TypeScript.
The Express server itself is pretty simple:
First, we load
dotenv and then we expose our
SERVER_URL to both NextJS and Payload. Prefixing environment variables with
NEXT_PUBLIC_ will ensure that the variable is accessible within NextJS components, and similarly, prefixing a variable with
PAYLOAD_PUBLIC_ will expose the variable to Payload’s admin panel.
On line 27, we perform different actions based on if the
NEXT_BUILD environment variable is set. We do this as a nice-to-have because your Next app is going to be relying on your Payload APIs, especially if it has any static page generation to do. When you go to build your Next app, you probably also need your Payload server to be running.
So, if the
NEXT_BUILD variable is set, we start up your Express server for you before allowing Next to build. If it’s unset, we just go ahead and prepare the Next app as usual—then fire up the Express server. Easy peasy.
Payload comes with extremely versatile field types that allow you to model any type of data that you need. One of the most capable types is the Block field — and with it, you can allow your content editors to build completely dynamic page layouts with a super streamlined interface right within the Payload admin panel. Admins can then add, remove, and reorder blocks easily based on predefined components that you provide them with.
In this boilerplate, we’ve pictured how you can even write your Payload block configs directly in the same file as their React component counterparts. You could even go so far as to re-use your frontend website’s React component that shows the data saved within Payload’s admin panel itself to edit that same data. There is a ton of potential here.
For example, check out the Call to Action block in this repo.
In that one file, we define the following:
These things don’t all need to be in the same file, but if you want to, Payload allows for it. Both NextJS and Payload support transpiling JSX within their files. You should be able to write your projects however you want.
Here’s how that
CallToAction block looks in the Admin panel:
And here’s how it looks in the minimally styled NextJS frontend:
Actually going to render the blocks themselves in React is also pretty trivial:
The above component accepts a
layout prop that is typed to an array of Payload blocks. The component then maps over the provided blocks and selects a block from those provided by the
blockType of each block in the array. Props are provided, and the block is rendered! Beautiful. So simple, and so much power.
This boilerplate comes with an optional seed script which can be run via
yarn seed or
npm run seed.
It automatically creates one Media document (which uploads and formats a JPG) and two sample Page documents that demonstrate a few Blocks in action.
Payload’s Local API is extremely powerful. It’s got a ton of use cases—including retrieving documents directly on the server within custom routes or within NextJS’
getServerSideProps as seen in the Page component within this boilerplate. It’s super fast, because there’s no HTTP layer: it’s not a typical REST API call or a GraphQL query. It never leaves your server and returns results in a handful of milliseconds, and it’s even faster if you’re running a local MongoDB instance. You thought NextJS server-rendering was fast? Try it when you don’t even need to leave your server to get your data. That’s fast.
You can also use the Local API completely separately from your running server within separate Node scripts.
local: true to Payload’s
init() call, Payload will skip setting up the REST and GraphQL APIs and only expose its Local API operations. Perfect for seed scripts and similar programmatic activities like batch-sending emails to customers, migrating your data from one shape to another, manually syncing Customer records to a CRM, etc.
Here’s the seed script that comes with this boilerplate:
Pretty awesome stuff.
If you plan to
next export a fully static version of your NextJS site, then the value of this boilerplate diminishes a bit and you should likely keep your front + backend entirely separate from each other. In this case, the only strength that this approach offers is that you can host your CMS and your app itself on one server, with one deployment. If possible, in this case, you might want to consider deploying your statically exported site on a CDN-friendly host like Netlify, Vercel, or even an S3 bucket, and host your Payload instance on DigitalOcean, Heroku, or similar.
We plan to release many more boilerplates in the future, so if this one doesn’t make sense for your needs, make sure to follow along with us to keep up with everything we’ve got coming out, including examples for how to use Payload alongside of a fully static site exported with Next, built with Gatsby, or other similar tactics.
With this boilerplate, you can build fully featured NextJS sites and apps fully powered by a CMS. So get building! Define your own Collections that describe the shape of your data, make use of Payload’s Globals for items such as header and footer nav structures, or build a full user-authenticated app by relying on Payload’s extensible Authentication support.
If you’d rather start a blank Payload project, you can get started in one line:
From there, you’ll be prompted to choose between a few different starter templates in JS or TS.
It’s also super easy to build out a Payload project yourself from scratch.
Thank you for reading and keep an eye out for more!