Skip to content

Instantly share code, notes, and snippets.

@ChangoMan
Created August 21, 2023 06:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChangoMan/824daa0b4fbd6f824d2e8a2ab1532006 to your computer and use it in GitHub Desktop.
Save ChangoMan/824daa0b4fbd6f824d2e8a2ab1532006 to your computer and use it in GitHub Desktop.
Next.js App Router Sitemap.js
import { MetadataRoute } from 'next'
import { getAllArticles } from '@/lib/articles'
const WEBSITE_HOST_URL = process.env.SITE_URL || 'https://travelxfamily.com'
type changeFrequency =
| 'always'
| 'hourly'
| 'daily'
| 'weekly'
| 'monthly'
| 'yearly'
| 'never'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
let articles = await getAllArticles()
const changeFrequency = 'daily' as changeFrequency
const posts = articles.map(({ slug, date }) => ({
url: `${WEBSITE_HOST_URL}/posts/${slug}`,
lastModified: date,
changeFrequency,
}))
const routes = ['', '/about', '/posts'].map((route) => ({
url: `${WEBSITE_HOST_URL}${route}`,
lastModified: new Date().toISOString(),
changeFrequency,
}))
return [...routes, ...posts]
}
@LeMoussel
Copy link

What is @/lib/articles?

@ChangoMan
Copy link
Author

What is @/lib/articles?

import glob from 'fast-glob'

interface Article {
  title: string
  description: string
  author: string
  date: string
}

export interface ArticleWithSlug extends Article {
  slug: string
}

export async function importArticle(
  articleFilename: string,
): Promise<ArticleWithSlug> {
  let { article } = (await import(`../app/posts/${articleFilename}`)) as {
    default: React.ComponentType
    article: Article
  }

  return {
    slug: articleFilename.replace(/(\/page)?\.mdx$/, ''),
    ...article,
  }
}

export async function getAllArticles() {
  let articleFilenames = await glob('*/page.mdx', {
    cwd: './src/app/posts',
  })

  let articles = await Promise.all(articleFilenames.map(importArticle))

  return articles.sort((a, z) => +new Date(z.date) - +new Date(a.date))
}

@tr1s
Copy link

tr1s commented Jan 9, 2024

how come this only works in local and not prod? @ChangoMan

@tyrsday29
Copy link

how come this only works in local and not prod? @ChangoMan

could be because you're not using the correct Environment Variable prefix, NEXT_PUBLIC goes in front of environment variables

@ChangoMan
Copy link
Author

how come this only works in local and not prod? @ChangoMan

@tr1s This should be working in prod, at website.com/sitemap.xml. For example: https://travelxfamily.com/sitemap.xml
Do you have a repo of your code that you can share?

@tr1s
Copy link

tr1s commented Apr 2, 2024

I'm pretty sure the reason was because the server/Netlify can't read the filesystem (aka my static Next pages). My environment variables were correct, that wasn't the issue.

This ended up being what I wrote to get my static pages:

import glob from 'fast-glob';

export async function getAllStaticPageSlugs() {
	let staticPageFilenames = await glob('**/*.js', {
		cwd: './pages',
		ignore: ['api/**/*', '_app.js', '_document.js', '404.js', 'index.js', 'testing.js'],
	});

	let staticPageSlugs = staticPageFilenames.map((filename) => filename.replace(/\.js$/, ''));

	return staticPageSlugs;
}

Then I had a bunch of other logic to get my Contentful pages and filter out static pages that used Contentful.

Then at the end I had to write the sitemap to the public folder so Netlify could read it:

// Write the sitemap to a file in the public directory.
fs.writeFileSync(path.join(process.cwd(), 'public', 'sitemap.xml'), sitemap);

...so yeah I have no idea how y'all got this to work in prod 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment