Skip to content

Build failure persists with params as Promise in Next.js App Router, possibly related to locale handling #8711

@danielkoller

Description

@danielkoller

Link to reproduction

No response

Environment Info

Payload: 3.0.0-beta.113
Node.js: 22.9.0
Next.js: 15.0.0-canary.173

Describe the Bug

Despite implementing the suggested changes from issue #8463, the build process continues to fail when using Payload CMS with Next.js App Router. The problem seems to be related to how params is being passed to page components and metadata generation functions. I suspect that the handling of the locale parameter might be contributing to this issue.

When trying to run the build, I get this error ->

Type error: Type '{ params: { locale: string; }; }' does not satisfy the constraint 'PageProps'. Types of property 'params' are incompatible. Type '{ locale: string; }' is missing the following properties from type 'Promise<SegmentParams>': then, catch, finally, [Symbol.toStringTag]

Here is my localized [slug]-page handling the pages created via Payload CMS.

import type { Metadata } from 'next'
import { notFound } from 'next/navigation'
import { PayloadRedirects } from '@/components/PayloadRedirects'
import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { draftMode } from 'next/headers'
import React, { cache } from 'react'
import type { Page as PageType } from 'src/payload-types.ts'
import { RenderBlocks } from '@/blocks/RenderBlocks'

import { generateMeta } from '@/utilities/generateMeta'

type Args = {
  params: Promise<{
    slug?: string
    locale: string
  }>
}

export default async function Page({ params }: Args) {
  const { slug = 'home', locale } = await params
  const url = `/${locale}/${slug}`
  const { isEnabled: isDraftMode } = await draftMode()

  let page: PageType | null

  page = await queryPageBySlug({
    slug,
    locale,
    isDraftMode,
  })

  if (!page || (locale === 'en' && page._status !== 'published' && !isDraftMode)) {
    return notFound()
  }

  return (
    <article className="container pb-24">
      <PayloadRedirects disableNotFound url={url} />
      <RenderBlocks blocks={page.layout} />
    </article>
  )
}

export async function generateMetadata({ params }: Args): Promise<Metadata> {
  const { slug = 'home', locale } = await params
  const { isEnabled: isDraftMode } = await draftMode()
  const page = await queryPageBySlug({
    slug,
    locale,
    isDraftMode,
  })

  if (!page || (locale === 'en' && page._status !== 'published' && !isDraftMode)) {
    return {}
  }

  return generateMeta({ doc: page })
}

const queryPageBySlug = cache(
  async ({ slug, locale, isDraftMode }: { slug: string; locale: string; isDraftMode: boolean }) => {
    const payload = await getPayloadHMR({ config: configPromise })

    const page = await payload.find({
      collection: 'pages',
      where: {
        slug: {
          equals: slug,
        },
        ...(locale === 'en' && !isDraftMode ? { _status: { equals: 'published' } } : {}),
      },
      limit: 1,
      depth: 2,
      locale: locale as 'en' | 'de',
      draft: isDraftMode,
    })

    return page.docs[0]
  },
)

Reproduction Steps

  1. Set up a Next.js project with App Router and Payload CMS integration.

  2. Implement internationalization (i18n) in your Next.js app with Next-Intl.

  3. Create a dynamic route file for handling pages created via Payload CMS, e.g., app/[locale]/(frontend)[slug]/page.tsx.

  4. In this file, implement the page component and metadata generation as follows:

    import type { Metadata } from 'next'
    import { notFound } from 'next/navigation'
    import { PayloadRedirects } from '@/components/PayloadRedirects'
    import configPromise from '@payload-config'
    import { getPayloadHMR } from '@payloadcms/next/utilities'
    import { draftMode } from 'next/headers'
    import React, { cache } from 'react'
    import type { Page as PageType } from 'src/payload-types.ts'
    import { RenderBlocks } from '@/blocks/RenderBlocks'
    import { generateMeta } from '@/utilities/generateMeta'
    
    type Args = {
      params: Promise<{
        slug?: string
        locale: string
      }>
    }
    
    export default async function Page({ params }: Args) {
      const { slug = 'home', locale } = await params
      // ... rest of the component
    }
    
    export async function generateMetadata({ params }: Args): Promise<Metadata> {
      const { slug = 'home', locale } = await params
      // ... rest of the function
    }
    
    // ... queryPageBySlug function
  5. Ensure that your next.config.js is properly configured for i18n and Payload CMS.

import { withPayload } from '@payloadcms/next/withPayload'
import createNextIntlPlugin from 'next-intl/plugin'
import { withSentryConfig } from '@sentry/nextjs'
import redirects from './redirects.js'

const withNextIntl = createNextIntlPlugin()

const NEXT_PUBLIC_SERVER_URL = process.env.NEXT_PUBLIC_SERVER_URL || 'http://localhost:3000'

/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/i,
      use: ['@svgr/webpack'],
    })

    return config
  },
  images: {
    remotePatterns: [
      ...[NEXT_PUBLIC_SERVER_URL].map((item) => {
        const url = new URL(item)
        return {
          hostname: url.hostname,
          protocol: url.protocol.replace(':', ''),
        }
      }),
    ],
  },
  reactStrictMode: true,
  redirects,
}

// Combine withNextIntl, withPayload, and withSentryConfig
const combinedConfig = withSentryConfig(withNextIntl(withPayload(nextConfig)), {
  // Sentry config options
  org: 'organization-name',
  project: 'project-name',
  silent: !process.env.CI,
  widenClientFileUpload: true,
  reactComponentAnnotation: {
    enabled: true,
  },
  tunnelRoute: '/monitoring',
  hideSourceMaps: true,
  disableLogger: true,
  automaticVercelMonitors: true,
})

export default combinedConfig
  1. Run next build to start the build process. It fails with this error ->
Type error: Type '{ params: { locale: string; }; }' does not satisfy the constraint 'PageProps'.
  Types of property 'params' are incompatible.
    Type '{ locale: string; }' is missing the following properties from type 'Promise<SegmentParams>': then, catch, finally, [Symbol.toStringTag]

Adapters and Plugins

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions