Next.js Functions

 

Using NEXT.js with Headless CMS: Contentful, Prismic, and Sanity

In the realm of modern web development, creating performant and dynamic websites is a top priority. One approach gaining momentum is the combination of NEXT.js, a popular React framework for building server-rendered applications, with headless CMS platforms such as Contentful, Prismic, and Sanity. This combination allows developers to seamlessly integrate powerful content management capabilities with the flexibility and speed of NEXT.js. In this article, we’ll explore how to harness the potential of these tools to create cutting-edge websites.

Using NEXT.js with Headless CMS: Contentful, Prismic, and Sanity

1. Understanding the Headless CMS Concept

Traditional content management systems (CMS) often couple the content creation and management process tightly with the website’s frontend. This can lead to limitations in design flexibility, performance, and scalability. Headless CMS, on the other hand, decouple the content management and delivery systems. They provide a backend for content creation and storage while allowing developers to build frontend applications using their preferred tools and frameworks.

1.1. Benefits of Using NEXT.js with Headless CMS

The combination of NEXT.js and headless CMS platforms offers several benefits:

  • Performance: NEXT.js provides server-side rendering and optimized routing out of the box, leading to faster page loads and better search engine optimization (SEO).
  • Flexibility: Developers have full control over the frontend, enabling them to design unique user interfaces and experiences without being restricted by CMS-imposed templates.
  • Content Management: Headless CMS platforms offer intuitive content creation interfaces for non-technical users, streamlining the content management process.
  • Collaboration: Developers and content creators can work simultaneously without interfering with each other’s workflows.
  • Scalability: Headless architecture allows for easily scaling the frontend and backend separately, ensuring a seamless user experience even with increased traffic.

2. Setting Up a NEXT.js Project

Before integrating a headless CMS, let’s set up a basic NEXT.js project.

  1. Install Node.js if you haven’t already.
  2. Open your terminal and run:
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
bash
npx create-next-app my-cms-app
cd my-cms-app
npm install
npm run dev
bash npx create-next-app my-cms-app cd my-cms-app npm install npm run dev
bash
npx create-next-app my-cms-app
cd my-cms-app
npm install
npm run dev

Your NEXT.js app should now be running on http://localhost:3000.

3. Integrating Contentful with NEXT.js

3.1. Installation and Configuration

Contentful is a headless CMS that offers a user-friendly interface for content creation and management.

1. Sign up for a Contentful account and create a new space.

2. Install the Contentful SDK in your project:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
bash
npm install contentful
bash npm install contentful
bash
npm install contentful

3. Create a .env.local file in your project’s root directory and add your Contentful space’s API keys:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
env
CONTENTFUL_SPACE_ID=your_space_id
CONTENTFUL_ACCESS_TOKEN=your_access_token
env CONTENTFUL_SPACE_ID=your_space_id CONTENTFUL_ACCESS_TOKEN=your_access_token
env
CONTENTFUL_SPACE_ID=your_space_id
CONTENTFUL_ACCESS_TOKEN=your_access_token

4. Fetching and Rendering Content

Let’s create a component that fetches and displays content from Contentful.

1. In the components directory, create a file named ContentfulContent.js.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
jsx
import { useEffect, useState } from 'react';
import { createClient } from 'contentful';
const ContentfulContent = () => {
const [content, setContent] = useState('');
useEffect(() => {
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});
client.getEntry('your_entry_id')
.then((entry) => setContent(entry.fields.content))
.catch(console.error);
}, []);
return <div>{content}</div>;
};
export default ContentfulContent;
jsx import { useEffect, useState } from 'react'; import { createClient } from 'contentful'; const ContentfulContent = () => { const [content, setContent] = useState(''); useEffect(() => { const client = createClient({ space: process.env.CONTENTFUL_SPACE_ID, accessToken: process.env.CONTENTFUL_ACCESS_TOKEN, }); client.getEntry('your_entry_id') .then((entry) => setContent(entry.fields.content)) .catch(console.error); }, []); return <div>{content}</div>; }; export default ContentfulContent;
jsx
import { useEffect, useState } from 'react';
import { createClient } from 'contentful';

const ContentfulContent = () => {
  const [content, setContent] = useState('');

  useEffect(() => {
    const client = createClient({
      space: process.env.CONTENTFUL_SPACE_ID,
      accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
    });

    client.getEntry('your_entry_id')
      .then((entry) => setContent(entry.fields.content))
      .catch(console.error);
  }, []);

  return <div>{content}</div>;
};

export default ContentfulContent;

2. Import and use the ContentfulContent component in the pages/index.js file:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
jsx
import ContentfulContent from '../components/ContentfulContent';
const Home = () => {
return (
<div>
<h1>Welcome to My CMS App</h1>
<ContentfulContent />
</div>
);
};
export default Home;
jsx import ContentfulContent from '../components/ContentfulContent'; const Home = () => { return ( <div> <h1>Welcome to My CMS App</h1> <ContentfulContent /> </div> ); }; export default Home;
jsx
import ContentfulContent from '../components/ContentfulContent';

const Home = () => {
  return (
    <div>
      <h1>Welcome to My CMS App</h1>
      <ContentfulContent />
    </div>
  );
};

export default Home;

By following these steps, your NEXT.js app will fetch and render content from Contentful.

5. Building Dynamic Websites with Prismic and NEXT.js

5.1. Prismic Setup and Configuration

Prismic is another popular headless CMS known for its content modeling flexibility.

1. Sign up for a Prismic account and create a new repository.

2. Install the Prismic API package:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
bash
npm install prismic-javascript
bash npm install prismic-javascript
bash
npm install prismic-javascript

3. In your .env.local file, add your Prismic API endpoint:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
env
PRISMIC_API_ENDPOINT=your_api_endpoint
env PRISMIC_API_ENDPOINT=your_api_endpoint
env
PRISMIC_API_ENDPOINT=your_api_endpoint

6. Querying and Displaying Content

Now, let’s create a dynamic page using content from Prismic.

1. Create a file named DynamicPage.js in the components directory.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
jsx
import { RichText } from 'prismic-reactjs';
import { Client } from '../prismic-configuration';
const DynamicPage = ({ document }) => {
const { data } = document;
return (
<div>
<h1>{RichText.asText(data.title)}</h1>
<div>{RichText.render(data.content)}</div>
</div>
);
};
export default DynamicPage;
jsx import { RichText } from 'prismic-reactjs'; import { Client } from '../prismic-configuration'; const DynamicPage = ({ document }) => { const { data } = document; return ( <div> <h1>{RichText.asText(data.title)}</h1> <div>{RichText.render(data.content)}</div> </div> ); }; export default DynamicPage;
jsx
import { RichText } from 'prismic-reactjs';
import { Client } from '../prismic-configuration';

const DynamicPage = ({ document }) => {
  const { data } = document;

  return (
    <div>
      <h1>{RichText.asText(data.title)}</h1>
      <div>{RichText.render(data.content)}</div>
    </div>
  );
};

export default DynamicPage;

2. Fetch and display Prismic content in the pages/dynamic/[uid].js file:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
jsx
import { useRouter } from 'next/router';
import { Client } from '../../prismic-configuration';
import DynamicPage from '../../components/DynamicPage';
const DynamicContent = ({ document }) => {
return <DynamicPage document={document} />;
};
export async function getServerSideProps({ params }) {
const { uid } = params;
const document = await Client.getByUID('page', uid);
return {
props: { document },
};
}
export default DynamicContent;
jsx import { useRouter } from 'next/router'; import { Client } from '../../prismic-configuration'; import DynamicPage from '../../components/DynamicPage'; const DynamicContent = ({ document }) => { return <DynamicPage document={document} />; }; export async function getServerSideProps({ params }) { const { uid } = params; const document = await Client.getByUID('page', uid); return { props: { document }, }; } export default DynamicContent;
jsx
import { useRouter } from 'next/router';
import { Client } from '../../prismic-configuration';
import DynamicPage from '../../components/DynamicPage';

const DynamicContent = ({ document }) => {
  return <DynamicPage document={document} />;
};

export async function getServerSideProps({ params }) {
  const { uid } = params;
  const document = await Client.getByUID('page', uid);

  return {
    props: { document },
  };
}

export default DynamicContent;

With these steps, you’ve created a dynamic page that fetches and renders content from Prismic.

7. Real-time Collaboration with Sanity and NEXT.js

7.1. Sanity Installation and Configuration

Sanity offers real-time collaborative content editing and is known for its customizable schema.

1. Sign up for a Sanity account and create a new project.

2. Install the Sanity CLI globally:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
bash
npm install -g @sanity/cli
bash npm install -g @sanity/cli
bash
npm install -g @sanity/cli

3. Initialize a new Sanity project:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
bash
sanity init
bash sanity init
bash
sanity init

Follow the prompts to set up your project.

8. Real-time Editing and Preview

Let’s create a blog post editing and previewing experience with Sanity.

1. In the schemas directory of your Sanity project, create a new schema file named post.js:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
javascript
export default {
name: 'post',
type: 'document',
title: 'Blog Post',
fields: [
{
name: 'title',
type: 'string',
title: 'Title',
},
{
name: 'content',
type: 'array',
title: 'Content',
of: [{ type: 'block' }],
},
],
};
javascript export default { name: 'post', type: 'document', title: 'Blog Post', fields: [ { name: 'title', type: 'string', title: 'Title', }, { name: 'content', type: 'array', title: 'Content', of: [{ type: 'block' }], }, ], };
javascript
export default {
  name: 'post',
  type: 'document',
  title: 'Blog Post',
  fields: [
    {
      name: 'title',
      type: 'string',
      title: 'Title',
    },
    {
      name: 'content',
      type: 'array',
      title: 'Content',
      of: [{ type: 'block' }],
    },
  ],
};

2. Create a new folder named posts in the documents directory and add a new blog post.

3. Start the Sanity studio:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
bash
sanity start
bash sanity start
bash
sanity start

4. Create a file named SanityPreview.js in the components directory:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
jsx
import { useState, useEffect } from 'react';
import { LivePreview } from 'sanity-react-live-preview';
import sanityClient from '@sanity/client';
const client = sanityClient({
projectId: 'your_project_id',
dataset: 'your_dataset',
useCdn: true,
});
const SanityPreview = ({ documentId }) => {
const [url, setUrl] = useState('');
useEffect(() => {
client
.getUrl(documentId)
.then((url) => setUrl(url))
.catch(console.error);
}, [documentId]);
return <LivePreview url={url} />;
};
export default SanityPreview;
jsx import { useState, useEffect } from 'react'; import { LivePreview } from 'sanity-react-live-preview'; import sanityClient from '@sanity/client'; const client = sanityClient({ projectId: 'your_project_id', dataset: 'your_dataset', useCdn: true, }); const SanityPreview = ({ documentId }) => { const [url, setUrl] = useState(''); useEffect(() => { client .getUrl(documentId) .then((url) => setUrl(url)) .catch(console.error); }, [documentId]); return <LivePreview url={url} />; }; export default SanityPreview;
jsx
import { useState, useEffect } from 'react';
import { LivePreview } from 'sanity-react-live-preview';
import sanityClient from '@sanity/client';

const client = sanityClient({
  projectId: 'your_project_id',
  dataset: 'your_dataset',
  useCdn: true,
});

const SanityPreview = ({ documentId }) => {
  const [url, setUrl] = useState('');

  useEffect(() => {
    client
      .getUrl(documentId)
      .then((url) => setUrl(url))
      .catch(console.error);
  }, [documentId]);

  return <LivePreview url={url} />;
};

export default SanityPreview;

5. Fetch and display the Sanity preview in the pages/sanity-preview.js file:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
jsx
import { useRouter } from 'next/router';
import SanityPreview from '../components/SanityPreview';
const SanityPreviewPage = () => {
const router = useRouter();
const { id } = router.query;
if (!id) return null;
return <SanityPreview documentId={id} />;
};
export default SanityPreviewPage;
jsx import { useRouter } from 'next/router'; import SanityPreview from '../components/SanityPreview'; const SanityPreviewPage = () => { const router = useRouter(); const { id } = router.query; if (!id) return null; return <SanityPreview documentId={id} />; }; export default SanityPreviewPage;
jsx
import { useRouter } from 'next/router';
import SanityPreview from '../components/SanityPreview';

const SanityPreviewPage = () => {
  const router = useRouter();
  const { id } = router.query;

  if (!id) return null;

  return <SanityPreview documentId={id} />;
};

export default SanityPreviewPage;

With these steps, you’ve set up a real-time preview of a Sanity document using NEXT.js.

9. Choosing the Right Headless CMS for Your Project

The choice of headless CMS depends on your project’s requirements, team familiarity, and content management needs.

  • Contentful: Great for structured content and robust APIs. Suited for larger projects with complex content relationships.
  • Prismic: Offers a flexible content model and a rich text editor. Ideal for projects requiring dynamic content.
  • Sanity: Provides real-time collaborative editing and schema customization. Best for projects prioritizing real-time collaboration.

Conclusion

Using NEXT.js with headless CMS platforms like Contentful, Prismic, and Sanity empowers developers to build high-performance, dynamic, and content-rich websites. The decoupled architecture allows for greater flexibility, scalability, and collaboration, while the unique features of each CMS cater to different project needs. Whether you’re looking for powerful APIs, customizable schemas, or real-time collaboration, the combination of NEXT.js and headless CMS opens up a world of possibilities for creating modern web experiences. So, why not dive in and explore this exciting combination for your next project? Happy coding!

Previously at
Flag Argentina
Brazil
time icon
GMT-3
Accomplished Senior Software Engineer with Next.js expertise. 8 years of total experience. Proficient in React, Python, Node.js, MySQL, React Hooks, and more.