This website uses cookies

Our website, platform and/or any sub domains use cookies to understand how you use our services, and to improve both your experience and our marketing relevance.

Next.js Headless CMS: What It Is and How to Build One

Updated on May 20, 2026

11 Min Read

Key Takeaways

  • A headless CMS keeps content separate, and Next.js turns it into fast static pages that load quickly and help SEO.
  • Since the CMS is separate from the frontend, there’s less exposure and you get full freedom over how the UI is built.
  • You don’t need a Node.js server for this setup, Next.js can output static files that run on any normal web server.

In a headless CMS, content is stored and managed separately from the frontend. A lot of modern blogs and eCommerce sites work this way. Content lives inside the CMS and gets sent to the frontend through an API.

Next.js works really well with headless setup because it can fetch content directly from the CMS and turn it into static pages. What this does is that it keeps the frontend fast and easier to manage compared to typical theme-based systems.

In this blog, I’ll cover why using Next.js with a headless CMS makes sense, and some of its benefits. Then I’ll build a small blog project that connects to Contentful to fetch and display articles locally. Lastly, I’ll also deploy the project on a live Cloudways server.

Why Use Next.js with a Headless CMS?

One of the main reasons I believe any developer pairs Next.js with a headless CMS is due to performance. Next.js can generate pages ahead of time during the build process.

So…the site doesn’t have to request content over and over again from the CMS whenever someone visits a page.

This approach is called SSG (Static Site Generation).

Because pages are already built before the user opens the site, page loading time stays fast. And, the server has less work to handle. It also has SEO benefits since search engines like Google can read the content of the page directly from the HTML instead of waiting for JavaScript to load everything.

And lastly, we can’t forget about flexibility. CMS handles content management, while Next.js handles frontend separately. This makes both sides easier to work with.

How to Build a Headless Blog Using Next.js and Contentful

To show you how a Next.js headless CMS works in practice, I’ll create a mini project. It’ll basically be a modern, clean blog frontend that automatically fetches and displays articles written inside Contentful.

What I’ll Be Using

  • Node.js
  • VS Code
  • Contentful (Free tier)
  • Next.js and Tailwind CSS

Step 1: Setting Up the Headless CMS (Contentful)

First things first, I need a backend to store my blog posts. For this tutorial, I’ll be using the free tier of Contentful.

After I sign up, Contentful asks if I want to use a template. To save time, I chose the Blog template so I don’t have to build a database structure from scratch.

To check I have everything I need in Contentful, I’ll go to the Content model tab. I can see a few models already created.

Contentful dashboard panel showing custom content model types including Author, Rich image, and SEO components.

But I just need the page – Blog post one, which has 10 predefined fields like title, SEO properties, and content.

Contentful models schema panel highlighting a red box over the page blog post component list item.

Okay cool. Now I’ll go to the Content tab and again, since I used a template, Contentful should’ve already generated sample blog posts. I just need to check that the status is set to Published so the data can be fetched by my app.

Contentful entries dashboard showing a column list of articles with a green published status tag.

I can see the status as published so everything is good to go!

And one last thing I’ll do before I leave Contentful is copy my API keys so my Next.js app can talk to it.

To do this, I’ll go to Settings > API keys, click Add API key.

Then I’ll add a name and description so I remember what this key is for later. And click the Add API Key button to generate the keys.

Create API Key modal window with the key name set to nextjs headless cms.

I’ll leave this tab open so I can copy Space ID and the Content Delivery API – access token later.

Contentful web interface setting pane listing values for Space ID and Content Delivery API access tokens.

Step 2: Setting Up the Next.js Project

Now I’ll set up my Next.js frontend. But…before I create that, I need Node.js on my machine.

In my case, I’m using my office laptop which has IT restrictions so I’ll download the standalone binary version of Node.js from nodejs.org.

Node.js official download page with a red arrow pointing to the Standalone Binary zip button.

Once the download is complete, I’ll unzip the file in my downloads folder.

Windows file explorer downloads folder showing a zipped folder archive named node-v24.15.0-win-x64.zip.

Windows file explorer showing extracted Node.js files including node.exe and npm files.

Opening the project folder in Command Prompt

Now I’ll create a folder for my project. I’ll call it something simple like headless-blog.

Windows desktop wallpaper interface showing a standard yellow folder icon labeled headless-blog.

Next, I’ll open up command prompt where I’ll be running all my commands.

So the first command I’ll run would be to get to my project folder:

cd C:\Users\abdulrehman\Desktop\headless-blog

Pointing Command Prompt to the Node binary

Now…since the command prompt has no idea where Node is on my machine, I’ll run this command with the path where I unzipped my Node folder:

set PATH=%PATH%;C:\Users\abdulrehman\Downloads\node-v24.15.0-win-x64\node-v24.15.0-win-x64

To confirm everything is working correctly, I’ll run these two commands:

node -v
npm -v

I can see the version numbers for Node and npm, which means everything is in order.

Windows command prompt window displaying successful version outputs for node and npm.

Creating the Next.js project

I’ll now create my Next.js project by running:

npx create-next-app@latest .

The reason why I added the dot at the end of the command is to ensure Next.js sets up the project inside my current headless-blog folder and not in a new one.

I’ll accept the default prompt that pops up right after running the npx create-next-app@latest . command by hitting enter, making sure Tailwind CSS is selected. I’ll use it to create a clean UI for my project.

Command line console window displaying successful initialization text for creating a new headless-blog project folder.

Once Next.js finishes installing all the dependencies, it shows a Success! message.

Next I need the official packages to connect to Contentful and render its rich text. And to get them, I’ll run this installation command:

npm install contentful @contentful/rich-text-react-renderer

Command prompt showing the installation completion text for the contentful rich-text-react-renderer package.

Step 3: Fetching Data and Building a Clean UI

Next I’ll open up my project folder in VS Code. To do this, I’ll go to File > Open Folder and then select my project folder.

Visual Studio Code welcome panel alongside the active headless-blog workspace directory tree.

Securing the API Keys

Alright, I have my project opened in VS code, and now I need to store the Contentful keys I generated earlier in my actual project. To do this, in the root of my project folder, I’ll create a new file called .env.local and paste the keys inside the file:

CONTENTFUL_SPACE_ID=your_space_id_here
CONTENTFUL_ACCESS_TOKEN=your_access_token_here

Visual Studio Code code editor displaying environment variables for Contentful space ID and access token.

Setting up the Contentful Client

Next, I’ll set up the connection between my Next.js app and Contentful. To do this, in VS code, inside the app folder, I’ll create another folder and call it lib. And inside this new folder, a file called contentful.ts.

Visual Studio Code panel displaying the contentful.ts file configuration code structure.

In the contentful.ts file, I’ll paste this code to initialize the client:

import { createClient } from 'contentful';

export const client = createClient({
  space: process.env.CONTENTFUL_SPACE_ID as string,
  accessToken: process.env.CONTENTFUL_ACCESS_TOKEN as string,
});

Building the Blog UI

Now I’ll open the app/page.tsx file and delete the default Next.js code. I’ll replace it with my own code for which I’ll use some Tailwind CSS classes to give my blog UI a clean layout.

In the code I added my content type ID (pageBlogPost) which I found by going in the Content model tab, then opening the page – Blog post model, and clicking the Copy ID button.

Contentful schema builder dashboard screen showing content fields for a custom page blog post component model.

By adding the ID to the code, I’m telling Next.js which database table to pull data from, making sure it fetches my blog posts instead of author bios or landing pages.

import { client } from './lib/contentful';
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';

export default async function Home() {
  // Fetch entries matching the template's exact ID
  const response = await client.getEntries({ content_type: 'pageBlogPost' });
  const posts = response.items;

  return (
    <main className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
      <div className="max-w-3xl mx-auto">
        <h1 className="text-4xl font-extrabold text-gray-900 mb-8 text-center">
          My Headless CMS Blog
        </h1>

        <div className="space-y-6">
          {posts.map((post: any) => (
            <article
              key={post.sys.id}
              className="bg-white p-6 rounded-lg shadow-sm border border-gray-100 hover:shadow-md transition-shadow"
            >
              <h2 className="text-2xl font-bold text-gray-800 mb-3">
                {post.fields.title}
              </h2>
              {/* Checking if there is content before rendering to prevent errors */}
              {post.fields.content && (
                <div className="text-gray-600 prose">
                  {documentToReactComponents(post.fields.content)}
                </div>
              )}
            </article>
          ))}
        </div>
      </div>
    </main>
  );
}

Visual Studio Code editor showing Next.js React code inside the page.tsx file layout.

Testing What We Built

Alright, time to test. I’m going to minimize VS code and hop back into command prompt and start the development server:

npm run dev

I’ll verify it started by opening my browser and going to http://localhost:3000.

Since I can see the exact sample posts that came with the Contentful template getting pulled and displayed in the UI I built, it means the headless connection is working perfectly.

The data is flowing from the backend (Contentful) directly into my frontend UI (Next.js).

Localhost browser window showing a web page titled Creating sustainable cities.

Step 4: Setting Up the Project for a Live Server

Since my project is working properly on local setup, I can deploy it on a live server. For this tutorial, I’ll use Cloudways.

If you also want to deploy on Cloudways, you can use the 3-day free trial we offer. Since I already have my server ready, I’ll just create a new PHP application.

Cloudways hosting panel applications list with a red arrow pointing to the Add Application button.

Application setup form with Custom App selected and name set to nextjs headless cms.

Once I have my PHP application ready on Cloudways, to make the Next.js project run properly on it, I’m not going to run a full Node.js backend on the server. Instead, I’m going to run a static export.

This way, Next.js generates regular static HTML files that can run on pretty much any web server.

To do this, I’ll open the next.config.ts in VS Code and add just one line inside the configuration block: output: ‘export’.

Like this:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: 'export',
};

export default nextConfig;'

Visual Studio Code panel pointing to a red arrow highlighting the output export line inside next.config.ts.

After saving the file, I’ll go back to the command prompt and stop the dev server. I’ll hit CTRL+C to stop it.

And then I’ll run the build command:

npm run build

While the command runs, Next.js will start to pull all the sample blog posts from Contentful and convert them into static HTML files. And once that is done, there’ll be a new out folder in my project.

Terminal command window showing a successful Next.js production build compilation output.

And if I check VS code now, I can see the out folder created.

Visual Studio Code file explorer view highlighting the static build directory named out.

This is the folder with the final site ready that I’ll upload onto my Cloudways server.

Step 5: Pushing the Project to GitHub

Before I push my code to Git, there is one quick fix I need to make to the out folder

By default, Next.js ignores this folder when saving to Git. And since Cloudways needs this folder to display the site, I’ll just open the .gitignore file in VS Code and delete: /out/.

Visual Studio Code workspace panel pointing directly to the out line rule inside the gitignore configuration file.

Next, I’ll go to GitHub and create a repository.

GitHub repository creation page showing the repository name field filled with nextjs headless cms.

Once the repo ready, I’ll go back to command prompt and run these commands one at a time to upload my project to Git:

git init
git add .
git commit -m "First commit with static export"
git branch -M main
git remote add origin https://github.com/abdulrehman293/nextjs-headless-cms
git push -u origin main

Now I’ll go back to my GitHub repo and refresh. And as expected, all my files, including the out folder are uploaded to my repo.

GitHub repository directory page with a red arrow pointing to the out folder.

Step 6: Connecting Cloudways to GitHub

Now that the push to Git is done, I can pull the code to my Cloudways server. To do this, inside Cloudways, I’ll open my PHP application and select Deployment Via Git.

Next, I’ll click Generate SSH Keys and then View SSH Keys.

Cloudways server menu pointing to the Deployment via GIT tab and the Generate SSH Keys button.

Git remote address and branch configuration setup fields inside a cloud hosting panel.

I’ll leave this window open since I’ll copy the key from here and paste it into GitHub.

Generated public SSH RSA string text displayed in a modal pop-up box interface

Next, I’ll go back to my repository and click Settings, Deploy keys, and Add deploy key. I’ll paste the key I copied from Cloudways, give it a name like “Cloudways Server”, and click Add Key.

GitHub deploy keys setting page showing the copied public key pasted with the title Cloudways Key.

GitHub repository settings panel showing an added read-only active SSH credential titled Cloudways Key.

Step 7: Deploying the App and Going Live

We’re almost towards the end. I’ll go back to the Deployment Via Git page in Cloudways and add my Git Remote Address. I can find this address in GitHub when I click the Code button, select the SSH tab.

GitHub code dropdown window with the SSH tab open displaying a custom repository url address string.

I’ll paste that link into Cloudways and click Authenticate. After that, I’ll choose main in the branch field dropdown, and click Start Deployment.

Git remote repository address input field and branch settings window with main selected.

Cloudways will now pull all the code onto my server.

Green success alert notification box stating that the repository has been cloned.

Moment of Truth

After the deployment process finishes, the files will be on my server, but Cloudways will still look for the default index.php file in the main folder. Because of this, it’ll show me the default PHP page you get when you first launch a PHP app on Cloudways.

To fix this, I’ll point the server to my Next.js static files by clicking on Application Settings, then under the General tab, I’ll find the Webroot setting. Here, I’ll change public_html to public_html/out.

Application Settings dashboard menu pointing to a red circle highlighting the out path edit button.

And that’s it! Now if I click my Application URL, the site is live on Cloudways, displaying the exact same sample posts we fetched from Contentful earlier on the local setup.

Web browser window displaying a published blog post article titled Creating sustainable cities and the role of green urbanism.

Wrapping Up

That wraps up our guide on Next.js headless CMS. In this blog, I tried to cover exactly why this stack makes sense and the performance/security benefits you can get by separating your backend from the frontend.

I also showed you how to build a fully static Next.js frontend that automatically pulls data from Contentful on a local setup.

I also pushed the working project to my GitHub, and pulled it to my Cloudways server so I could test what I built on a live server.

If you also want to reuse my project, feel free to clone it and reuse the code. And if you want to test this deployment workflow on Cloudways, you can use our 3-day free trial.

Q. Is Next.js headless?

No, Next.js is a frontend framework. It is not a CMS. It is however used commonly with headless CMS platforms like Contentful or Sanity to display content on the frontend.

Q. What is the best headless CMS for Next.js?

This depends on your project. Contentful is easy to start with. Sanity gives you a bit more flexibility. Strapi is best if you want to host the CMS yourself.

Q. Does a headless CMS help with SEO?

Yes it does. A headless CMS stores the content, while Next.js renders the content into fast-loading pages that search engines can crawl more easily. So the easier the page to crawl, this can indirectly benefit your site’s SEO.

Q. Do I need a Node.js server to host Next.js?

Not always. If the project uses SSR or API routes, you’ll require Node.js. But for static exports, generated files can be hosted on a regular web server without running Node.js.

Share your opinion in the comment section. COMMENT NOW

Share This Article

Abdul Rehman

Abdul is a tech-savvy, coffee-fueled, and creatively driven marketer who loves keeping up with the latest software updates and tech gadgets. He's also a skilled technical writer who can explain complex concepts simply for a broad audience. Abdul enjoys sharing his knowledge of the Cloud industry through user manuals, documentation, and blog posts.

×

Webinar: How to Get 100% Scores on Core Web Vitals

Join Joe Williams & Aleksandar Savkovic on 29th of March, 2021.

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Want to Experience the Cloudways Platform in Its Full Glory?

Take a FREE guided tour of Cloudways and see for yourself how easily you can manage your server & apps on the leading cloud-hosting platform.

Start my tour