I'm trying to migrate my portfolio website (https://meje.dev) to use the recent version of Next.js and Typescript, plus a couple of improvements on my end too.
For my blog, I'm using next-mdx-remote and node's filesystem module to read posts from my project.
When I run the dev server locally, and I click on a PostCard to view the article, the dynamic route renders my markdown content.
When I run the build locally too, it runs quite fine and completes the entire step without errors. But when I try to access any article in production, I'm met with a 404 page. I'm very confused, because this is almost the same thing (except the typescript migration) that powers the current website
Here's the new site: https://sev-demo.vercel.app/
I've also tried adding a pageExtensions property to my nextConfig, but it's still the same.
Here's an excerpt of what my slug route looks like:
export type Post = { source: MDXRemoteSerializeResult< Record<string, unknown>, Record<string, unknown> >; tableOfContent: Heading; updatedAt: string; frontmatter: PostFrontmatter;};
export default function BlogPost({ post }: { post: Post }) { return ( <> <MetaData pageTitle={post.frontmatter.title} /> <Layout> <Box display="flex" flexFlow="column" gap="1em"> <Box className="content" pb="1.4em"> <MDXRemote {...post.source} /> </Box> </Box> </Layout> </> );}
export async function getStaticProps(context: GetStaticPropsContext) { const slug = context?.params && context?.params.slug;
const { content, frontmatter } = await getPostFromSlug(String(slug)); const markdownHeadings = extractHeadings(`posts/${slug}.mdx`); const lastModified = await getLastModified(`posts/${slug}.mdx`);
const posts = await getPosts();
const rpcOptions = { theme: "aurora-x", keepBackground: false, defaultLang: { block: "javascript", inline: "plaintext", }, filterMetaString: (str: string) => str.replace(/filename="[^"]*"/, ""), };
const mdxSource = await serialize(content, { mdxOptions: { rehypePlugins: [ rehypeSlug, [ rehypeAutolinkHeadings, { properties: { className: ["anchor"] }, }, { behaviour: "wrap" }, ], [rehypePrettyCode, rpcOptions], rehypeCodeTitles, ], }, });
return { props: { post: { frontmatter, source: mdxSource, tableOfContents: markdownHeadings, updatedAt: lastModified?.toDateString(), }, }, };}
export async function getStaticPaths() { // prerender all pages // (slower builds, but faster initial page load) // const paths = (await getPostSlugs()).map((slug) => ({ params: { slug } }));
return { paths: [], fallback: false, };}