I'm getting unexpected or undocumented results for getStaticProps with a dynamic route parameter. The params slug is an array of path parts (looks like path.split('/')). It occurs for me when the page is in a dynamic folder and named index.js and not with a spread filename ie [...slug].js. Here's an example:
Problem case:
// at pages/abc/[slug]/index.js
export async function getStaticProps({ params }) {
console.log(params); // --- @build: { slug: 'a' } @runtime: { slug: ['abc', 'a'] }
}
What I expected:
// at pages/abc/[slug]/index.js
export async function getStaticProps({ params }) {
console.log(params); // { slug: 'some-slug' }
}
I get these errors at runtime after deploying to now serverless. Builds are successful and the page appears to work but found the error in the now function logs.
Hi, can you provide a link to a repo with a reproduction for this? A param is an array when using catch-all routes [...slug] but should always be a string for normal dynamic routes [slug]
I'll work on a repro, I'm not using the [...slug] and this works at build. To note, I am using unstable_revalidate but I don't think that matters. I just checked and at build time I get the string param as expected but get the array at runtime. Making a page like this should be enough for a repro:
const Page = ({slug}) => <div>{slug}</div>
// at pages/abc/[slug]/index.js
export async function getStaticPaths() {
const paths = [{ params: { slug: 'a' } }, { params: { slug: 'b' } }];
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
const { slug } = params;
console.log('params', params); // --- @ build: { slug: 'a' } @ runtime: { slug: ['abc', 'a'] }
return {
props: { slug },
unstable_revalidate: 1,
};
export default Page;
I was unable to repro from a clean project. You'd need access to the now function logs to see the error, but I'd be happy to share the issue from my code base if I could do so privately.
The error occurs when getStaticProps is called at runtime but doesn't break the page. Also to note, I'm getting errors at client-side transitions where the params looks like this:
{
slug: [
'_next',
'data',
'yxBWbsufabv-YK1Rxk_-n',
'abc',
'some-slug.json'
]
}
I'm also happy to share my function logs in now, I'm seeing the same error.
@ijjk The issues doesn't occur when I change the folder to be the slug filename, this was my setup:
โโโ abc
โ โโโ [slug1]
โ โ โโโ [slug2]
โ โ โโโ index.js
โ โโโ index.js
This resolves it for the [slug2] param, but still an issue for the [slug1] param
โโโ abc
โ โโโ [slug1]
โ โ โโโ [slug2].js // no issue
โ โโโ index.js // still an issue
I'm struggling to repro. @mattpocock Do you happen to use next in a monorepo or with a custom now deployment? with builds or routes?
@ijjk I was finally able to reproduce the error. It seems to be cause by our custom now deployment. Here's a link showing the array for the params:
https://my-app-l93z01gn0.now.sh/abc/a
And the source code:
https://github.com/stevez86/next-repro
Should this be posted to the @now/next repo? Thanks for your help
@stevez86 we will need to investigate further the issue should be ok here for now, thanks for the reproduction!
@stevez86 Apologies for late reply. I was able to solve it with some routes config:
{
"version": 2,
"builds": [
{
"src": "project/next.config.js",
"use": "@now/next"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "/project/$1",
"continue": true
}
]
}
I was previously missing the 'continue' which screwed things up for me.
I'm in a monorepo, with a shared components lib (/subfolder-1) which the next js project (/subfolder-2) needs to work.
Thanks for your time.
@mattpocock I think you're referring to a different issue. I'm still getting an array of params with continue: true for a dynamic route that is in a folder (see repro)
@ijjk Do you know if this is going to be looked at anytime soon? It's blocking from using SSG and next 9.4. I'm happy to take a look at it and would appreciate it if you could point me to where the relevant routing takes place and I'll take a stab at finding the issue.
@stevez86 Are you using Yarn workspaces with your project by chance?
@pcwa-ahendricks I'm using workspaces and lerna
@stevez86 I had this same issue, with unexpected params populating the params object in the getStaticProps context. I used "continue: true" in my vercel.json so I do not believe that to be a reliable workaround. I used Yarn's workspaces feature with my project. The issue was hit and miss for me, but it eventually popped up after a certain number of route changes. I only had this issue with the catch-all dynamic routes. Additionally, I had an additional issue where when the params object did have clean values, ones similar to what I'd expect, there would be additional paths leading up to the catch-all path that were being included. For example, if my catch-all path is "/resource-library/[...slug]", sometimes when the routing did work it would include "resource-library" in the array for the slug prop which I believe is another issue.
Today I've changed my monorepo around so that it mimics the gatsby functions example structure, and I've elimited the use of Yarn's workspaces feature, and I've pared down my vercel.json so that in relies more on zero-config settings (I still have some 'rewrites', 'redirects', and 'headers' in vercel.json) by omitting the custom "builds" property. After doing so SSG is working as expected and I'm not seeing params similar to the ones you outlined above. I'm not asserting that this would be a good/available option for everyone, but it did simply my monorepo and fix this issue in doing so.
@pcwa-ahendricks Thanks for that information, it's helpful. How do you share components across projects? Currently I have this in my config, but only use it so I can run multiple "brands" from the same project; Searched a bit to end up at this config and not aware of any alternatives.
โโโ /brand1
โโโ /brand2
โโโ /shared
โโโ now.brand1.json
โโโ now.brand2.json
// ./now.brand1.json
{
"builds": [{ "src": "brand1/next.config.js", "use": "@now/next" }],
"routes": [{ "src": "/(.*)", "dest": "/brand1/$1", "continue": true }]
}
// ./now.brand2.json
{
"builds": [{ "src": "brand2/next.config.js", "use": "@now/next" }],
"routes": [{ "src": "/(.*)", "dest": "/brand2/$1", "continue": true }]
}
// ./package.json
"scripts": {
"deploy": "vc --local-config=now.$BRAND.json",
}
@stevez86 I don't share components per-se but I do share some common ts/js libs between the project /api folder and /src folder with the help of some settings which make importing easier defined in my tsconfig/jsconfig and eslintrc.
// .eslintrc.json
{
"settings": {
"import/resolver": {
"alias": {
"map": [
["@lib", "path/to/lib"]
]
}
}
}
}
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@lib/*": ["path/to/lib/*"]
}
}
}
However, I don't see any of that being a deal breaker. I think (not certain) you a stuck with having to define builds and routes in your now.json due to the other reasons you pointed out, namely cause there are multiple Nextjs projects in your monorepo. Do you have a separate package.json in /brand1 and /brand2 and so forth? Do you have separate Now aliases defined in each of those now.json files? If so, I wonder if you could run now from the context of each of those sub-folders instead of at the root level. This would of course create different deployments for each sub-folder, but maybe that would work better. I will admit that I'm not sure what the best practices for any of this are but my experiences with using Yarn workspaces with Now has always been rife with problems.
The importing config helps. I run the deployment from the root only because it uploads the shared and brand folders before the serverless build, otherwise those outside files are missing. I wonder if there is a way to build locally and deploy to vercel / now/ or a way to upload folders outside where the command is made. I haven't had success finding either of those. Ultimately I think the best fix is to fix the array params. It's only affecting those with the unstable_revalidate property so there isn't much I can do.
I'm also seeing this problem. I have a monorepo with the next codebase inside a folder. I'm also using unstable_revalidate and vercel
url: /abc/[:id]
params (build): {id: "123"}
params (preview): {id: ["abc", "123"]}
I was able to fix this for now by setting getStaticPaths to return paths:[]. Not optimal but works for now.
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: [],
fallback: true,
};
@ijjk @Timer I think this may be related... When I use useRouter() The result includes the expected pathname, route and query params but asPath is something like this: /brand/_next/data/c4LMdEPpk-2VWgkamXRRI/dir/[slug1]/[slug2].json
FYI this is for a setup simliar to the reproduction I linked earlier (monorepo) I was hoping / expecting it to match the url requested in the browser. Let me know if you need more info.
This also happens when using preview=true. I imagine the behaviour is similar to _unstable_revalidate
For anyone else stumbling on this, I wrote this ugly fix that seems to catch the edge cases I've hit so far:
const cleanNextParams = (queryParams: string[]) => {
let params = [...queryParams]
// Sort out next params
if (params.includes("_next")) {
const indexOfNext = params.findIndex((e) => e === "_next")
params.splice(indexOfNext, 3)
}
// remove .json from params
params = params.map((p) => p.replace(".json", ""))
return params
}
export const getStaticParam = (queryParam: string | string[], path: string, param: string) => {
if (typeof queryParam === "string") {
return queryParam
}
const indexOfParam = path.split("/").findIndex((el) => el === param)
if (indexOfParam < 0) {
return null
}
const cleanedParams = cleanNextParams(queryParam)
return cleanedParams[indexOfParam]
}
// pages/blog/[article].ts
export const getStaticProps = ({params}) => {
const path = "/blog/[article]"
const articleId = getStaticParam(params.article, path, "[article]")
}
@ijjk Do you have a rough ETA for when this will be fixed?
Ideally in this iteration: https://github.com/vercel/next.js/milestone/46
Hi @timneutkens sorry to pile on as well but wondered if you had an idea of the latest ETA on this. I'm hitting the same issue and approaching a production release and need to decide whether to implement a workaround or wait for the fix.
Most helpful comment
Ideally in this iteration: https://github.com/vercel/next.js/milestone/46