Open In App

Draft Mode Next.js

Last Updated : 01 Aug, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Draft Mode in Next.js enables content previewing and editing directly within your application, allowing content creators to view changes before publishing. This feature is especially useful for content management systems or any app where content updates need to be reviewed in real-time. We will explore what Draft Mode is, how to use it in your Next.js application, and best practices for its usage in this article.

Prerequisites:

What is Draft Mode?

Draft Mode in Next.js allows you to preview changes to your content before it is published. This is especially useful in a content management system (CMS) setup, where you want to see how new or edited content will look on your live website without actually publishing it. Draft Mode ensures that only authorized users can see the draft content, maintaining the integrity of the live site for regular visitors.

Working:

When Draft Mode is activated, a special cookie is set to indicate that draft content should be displayed. This allows Next.js to serve draft content instead of the published version.

Syntax:

pages/index.js
import { draftMode } from 'next/headers'
 
export default function Page() {
    const { isEnabled } = draftMode()
    return (
        <div>
            <p>Draft Mode is currently {isEnabled ? 'Enabled' : 'Disabled'}</p>
        </draft>
    )
}

Enable Draft Mode:

// pages/api/draft-enable/index.js
// for route/api/draft-inable

// route handler enabling draft mode
import { draftMode } from 'next/headers'
 
export async function GET(request) {
    draftMode().enable()
    return new Response('Draft mode is enabled')
}

Disable Draft Mode:

// pages/api/draft-disable/index.js

// for route/api/draft-disable import { draftMode } from 'next/headers' export async function GET(request: Request) { draftMode().disable() return new Response('Draft mode is disabled') }

Approach

To Implement the Draft Mode in Next.js

  • Create a new Next.js project using the create-next-app command.
  • Integrate a CMS that supports draft content, such as Contentful.
  • Set up an account and create an API key on the CMS.
  • Create an API route to enable Draft Mode and fetch draft content.
  • Modify data fetching logic to account for Draft Mode.
  • Enable Draft Mode during development by visiting the /api/enable-draft endpoint in the browser.

Steps to Create Next.js Project

Step 1: Create a nextJS application by using this command and navigate to project directory

npx create-next-app draftmode
cd draftmode

Step 2: Install the necessary packages/libraries in your project using the following commands.

npm install contentful

Project Structure:

draftmodestruct
draft mode project structure

The updated dependencies in package.json file will look like:

"dependencies": {
    "contentful": "^10.11.2",
    "next": "14.2.3",
    "react": "^18",
    "react-dom": "^18"
  }

Implementing Draft Mode

Step 1: Set up Contentful access credentials

After installing the Contentful package, visit the Contentful official website and sign up. In the settings, create a new API key with the name of your choice.

CONTENTFUL_SPACE_ID=your_space_id
CONTENTFUL_ACCESS_TOKEN=your_access_token
CONTENTFUL_PREVIEW_ACCESS_TOKEN=preview_token

Step 2: Replace Valid Token

Next, we will replace our actual valid tokens shown above in the .env.local file. We will also create an API route that enables Draft Mode and fetches draft content from Contentful, creating the Draft Mode API Route.

Step 3: Fetching Draft Content

Modify your data fetching logic to account for Draft Mode. For example, in getStaticProps, you can check if Draft Mode is enabled and fetch draft content accordingly. In this example, we are retrieving pageProduct content. Before fetching, make sure to create one with appropriate fields in the content Model of Contentful.

Example: Implementation to show draft mode.

JavaScript
// pages/api/enable-draft.js

export default function handler(req, res) {
  res.setDraftMode({ enable: true });
  res.end();
}
JavaScript
// pages/index.js

import { createClient } from "contentful";

const client = createClient({
  space: process.env.CONTENTFUL_SPACE_ID,
  accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
  ...(process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN && {
    host: "preview.contentful.com",
    accessToken: process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN,
  }),
});

export async function getStaticProps({ preview = false }) {
  try {
    const entries = await client.getEntries({
      content_type: "pageProduct",
      order: "-sys.createdAt",
      ...(preview && { preview: true }),
    });

    const products = entries.items.map((product) => {
      // Avoid fetching deeply nested 
      // related products to prevent circular references
      const relatedProducts =
        product.fields.relatedProducts?.map((relatedProduct) => {
          return {
            ...relatedProduct,
            fields: {
              ...relatedProduct.fields,
              // Set related products to an empty 
              // array to avoid circular references
              relatedProducts: [],
            },
          };
        }) || [];

      return {
        ...product,
        fields: {
          ...product.fields,
          relatedProducts: relatedProducts,
        },
      };
    });

    return {
      props: {
        products,
      },
    };
  } catch (error) {
    console.error("Error fetching Contentful entries:", error);
    return {
      props: {
        products: [],
      },
    };
  }
}

function HomePage({ products }) {
  return (
    <div>
      <h1>Products</h1>
      {products.map((product) => (
        <div key={product.sys.id}>
          <h2>{product.fields.name}</h2>
          <p>{product.fields.description}</p>
          <p>Price: ${product.fields.price}</p>
          {product.fields.featuredProductImage && (
            <img
              src={product.fields.featuredProductImage.fields.file.url}
              alt={product.fields.name}
            />
          )}
          {product.fields.productImages &&
            product.fields.productImages.map((image) => (
              <img
                key={image.sys.id}
                src={image.fields.file.url}
                alt={product.fields.name}
              />
            ))}
          {product.fields.relatedProducts &&
            product.fields.relatedProducts.length > 0 && (
              <div>
                <h3>Related Products</h3>
                {product.fields.relatedProducts.map((relatedProduct) => (
                  <p key={relatedProduct.sys.id}>
                    {relatedProduct.fields.name}
                  </p>
                ))}
              </div>
            )}
        </div>
      ))}
    </div>
  );
}

export default HomePage;

Step 4: Enabling Draft Mode in Development

To enable Draft Mode during development, visit the /api/enable-draft endpoint in your browser. This will set a cookie that enables Draft Mode for your session.

enabledraftmode
enter this URL in the browser

DraftMode session Output:

enableddraftmode
DraftMode session is created.

Best Practices for Using Draft Mode

  • Secure Access: Ensure that only authorized users can enable Draft Mode. This can be done by adding authentication checks in the API route.
  • Clear Indication of Draft Mode: Make it clear to the user that they are viewing draft content. This can be done by adding a banner or a notification at the top of the page.
  • Regularly Review Draft Content: Ensure that draft content is reviewed and published regularly to avoid stale drafts cluttering your CMS.

Conclusion

Draft Mode in Next.js is a powerful feature that allows developers and content editors to preview changes before they go live. By following the steps outlined in this guide, you can implement Draft Mode in your Next.js application, providing a seamless and secure way to preview draft content. Remember to secure access to Draft Mode and clearly indicate when it is enabled to ensure a smooth and professional workflow.


Next Article

Similar Reads

  翻译: