question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Preview does not work as intended with Contentful (localhost)

See original GitHub issue

Bug report

disclaimer: I have only tried running it locally, not sure if it would make a difference running on a live server

Describe the bug

I’ve implemented the preview API per instructions both from the NextJS documentation and Contentful Documentation. The issue I’m experiencing is that no preview cookies are set and the context.preview & preview.previewData is undefined. When I click on the preview button in contentful it hits the preview endpoint and redirects the user to the post correctly, so no errors occur.

However, if a simply refresh the page or if I go directly to preview API endpoint with the correct params then the cookies are set and the preview is working as expected.

To Reproduce

  1. Setup a contentful space (I’ve used the example that can be used to fill the space with some dummy content)
  2. Setup the preview API in contentful (Link provided further down below)
  3. Implement the code I’ve used (see Code Snippets section)
  4. Try the “Open Preview” button

Expected behavior

When I click the “Open Preview” button I should be able to see the content with the unpublished changes.

Code Snippets

// pages/api/preview.ts

import { NextApiRequest, NextApiResponse } from 'next'
import { Entry } from 'contentful';

// Services
import { initContentful } from '../../services/contentful'

// Interfaces
import { BlogPost } from '../../interfaces/BlogPost';


const getEntryUrlPrefix = (entry: Entry<BlogPost>) => {
    switch(entry.sys.contentType.sys.id) {
        case 'blogPost':
            return '/blog/'
        default:
            return '';
    }
}

const getEntryPath = (entry: Entry<BlogPost>) => {
    if ( typeof entry.fields.slug !== 'undefined' ) {
        return entry.fields.slug
    }

    return entry.sys.id
}


export default async (req: NextApiRequest, res: NextApiResponse) => {

    // Check the secret - This secret should only be known to this API route and the CMS    
   // The secret should not be the same as your preview access token
    if (process.env.CONTENTFUL_PREVIEW_SECRET !== req.query.secret) {
        return res.status(401).json({ message: 'Invalid secret' })
    }

    // Check params
    if (!req.query.id) {
        return res.status(404).json({ message: 'You must pass an id' })
    }

    // Fetch the headless CMS to check if an entry with the provided `id` exists
    const id = Array.isArray(req.query.id) ? req.query.id[0] : req.query.id;
    
    try {

        const cf = await initContentful({ preview: true })
        const entry = await cf.getEntry<BlogPost>(id);

        // If the slug doesn't exist prevent preview mode from being enabled
        if (!entry) {
            return res.status(404).json({ message: `No entry was found with id: ${id}` })
        }

        const prefix = getEntryUrlPrefix(entry)
        const path = getEntryPath(entry)

        // Enable Preview Mode by setting the cookies
        res
            .setPreviewData({})
            .writeHead(307, { Location: `${prefix}${path}` })
            .end('Preview mode enabled')


    } catch (error) {

        return res.status(500).json({ message: error.message })

    }

}
// pages/blog/[slug].tsx

// This function gets called at build time
export const getStaticPaths: GetStaticPaths = async () => {
    
    const cf = await initContentful()
    const blogPosts = await cf.getEntries<BlogPost>({
        content_type: ContentfulContentTypes.blogPost
    })

    // Get the paths we want to pre-render based on posts
    const paths = blogPosts.items.map(blogPost => ({
        params: { slug: blogPost.fields.slug },
    }))

    // We'll pre-render only these paths at build time.
    // { fallback: false } means other routes should 404.
    return { paths, fallback: true }

}

export const getStaticProps: GetStaticProps = async (context) => {

    if (!context.params) {
        return { props: { errors: 'Ops no parameters was given' } }
    }

    console.log('getStaticProps', context) // <-- This returns an object with the params but not with preview or previewData in the case I've described

    try {
        const { slug } = context.params
        const slugPath = Array.isArray(slug) ? slug[0] : slug
        const cf = await initContentful({ preview: context.preview ? true : false })
        const blogPost = await cf.getEntries<BlogPost>({
            content_type: ContentfulContentTypes.blogPost,
            'fields.slug': slugPath
        })

        if ( blogPost.total > 0) {
            return {
                props: { 
                    blogPost: blogPost.items[0],
                    preview: context.preview ? true : false
                }
            }
        }
        
        return { props: { errors: `No blog post was found with slug ${slugPath}` }}
    
    } catch (err) {
        return { props: { errors: err.message } }
    }

}
// services/contentful.ts

import { createClient, CreateClientParams } from 'contentful'

interface Options {
    preview?: boolean
}

export async function initContentful({ preview }: Options = { preview: false }) {
    try {
        const params: CreateClientParams = {
            space: process.env.CONTENTFUL_SPACE_ID,
            accessToken: preview ? process.env.CONTENTFUL_PREVIEW_TOKEN : process.env.CONTENTFUL_ACCESS_TOKEN,
            host: preview ? 'preview.contentful.com' : 'cdn.contentful.com',
        }
        const client = createClient(params)
        return client; 
    } catch (error) {
        throw error
    }       
}

Preview Url settings in contentful http://localhost:3000/api/preview?id={entry.sys.id}&secret=MY_SECRET

System information

  • OS: macOS
  • Browser: Chrome
  • Next.js: 9.3.1
  • Node: v13.10.1

Additonal Context

{
  "name": "with-typescript",
  "version": "1.0.0",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "type-check": "tsc"
  },
  "dependencies": {
    "@contentful/rich-text-react-renderer": "^13.4.0",
    "@contentful/rich-text-types": "^14.1.0",
    "contentful": "^7.14.0",
    "isomorphic-unfetch": "3.0.0",
    "next": "^9.3.1",
    "react": "^16.13.0",
    "react-dom": "^16.13.0",
    "sass": "^1.26.3"
  },
  "devDependencies": {
    "@types/node": "^13.9.2",
    "@types/react": "^16.9.23",
    "@types/react-dom": "^16.9.5",
    "dotenv": "^8.2.0",
    "typescript": "^3.8.3"
  },
  "license": "ISC"
}

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:1
  • Comments:11 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
dohomicommented, Mar 24, 2020

@patrickedqvist I think I have the exact same error. In development mode (localhost) preview and previewData is empty

1reaction
patrickedqvistcommented, Mar 20, 2020

contentful-nextjs-preview-bug

Read more comments on GitHub >

github_iconTop Results From Across the Web

Preview does not work as intended with Contentful (localhost)
The issue I'm experiencing is that no preview cookies are set and the context. preview & preview.
Read more >
Content preview | Contentful Help Center
This feature makes it possible to add a single or multiple environments, preview draft and published entries, and select which content types can...
Read more >
Step-by-Step Tutorial to Setup Contentful Preview Mode with ...
This article is a tutorial on how to set up content previews for a Contentful-based website built in Next.js.
Read more >
Vercel preview mode not working on prod, but works locally
trying to see draft pages from contentful doesnt work unless the page was previously published and caused a re-build. -My env is set...
Read more >
Next.js Blog with Preview Mode - Vercel
Your blog should be up and running on http://localhost:3000! If it doesn't work, post on GitHub discussions. Step 7. Try preview mode. In...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found