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.
https://github.com/payloadcms/payload/tree/main/examples/custom-server
This boilerplate includes the following:
getServerSideProps
Head
componentdotenv
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
files:
tsconfig.json
, which will be used for the entirety of your NextJS app, including all your React componentstsconfig.server.json
file, which will handle everything in the /server
folderYou’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 20, we then initialize Payload by passing a long and unguessable secret string used to secure Payload, a URL pointing to our MongoDB instance, and our newly instantiated Express app.
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.
The beauty of using a JavaScript library like React together with your Payload API means that you can write React components that map 1:1 with your blocks’ data. Your React components can accept the data that your editors author as props, and boom—your layouts are extremely well-organized and extensible well into the future.
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:
custom
or page
)CallToAction
block itselfThese 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:
/components/RenderBlocks/index.tsx
:
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.
By passing 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.
We want Payload to be the best CMS out there for modern JavaScript developers. Since our launch, we’ve received amazing feedback on our ideas and have had an awesome reception from the community, but we’re only getting started. We’d love to hear what you think. Leave a comment here with your thoughts, submit any issues or feature requests you might come across on our GitHub repo, or send us an email. For mission critical systems or complex projects, you may want direct support from the people who built Payload. If this is you, let us know!
Thank you for reading and keep an eye out for more!