diff --git a/src/app/blog/how-to-next-js-sitemap/page.mdx b/src/app/blog/how-to-next-js-sitemap/page.mdx
new file mode 100644
index 00000000..62d6b7c2
--- /dev/null
+++ b/src/app/blog/how-to-next-js-sitemap/page.mdx
@@ -0,0 +1,129 @@
+import { ArticleLayout } from '@/components/ArticleLayout'
+import { Button } from '@/components/Button'
+import Image from 'next/image'
+import Link from 'next/link'
+import nextJsSitemap from '@/images/next-js-sitemap.webp'
+import googleSearchConsole from '@/images/sitemap-google-search.webp'
+
+import { createMetadata } from '@/utils/createMetadata'
+
+export const metadata = createMetadata({
+ author: "Zachary Proser",
+ date: "2024-2-12",
+ title: "How to build a dynamic sitemap for your Next.js project",
+ description: "Steal my implementation to save yourself some time and headaches",
+ image: nextJsSitemap
+})
+
+export default (props) =>
+
+---
+
+
+
+At the time of this writing, there **are** [official docs on building a sitemap for your Next.js
+project](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/sitemap) that uses the newer app router pattern.
+
+However, they're on the sparse side, and there's more than a little confusion going around about
+how to pull this off based on the npm packages and GitHub issues I encountered when doing
+my own research.
+
+When you're finished this post, you'll have the knowledge and code you need to build a dynamic sitemap
+for your Next.js project that includes both 'static' routes such as `/about` or `/home`
+and dynamic routes such as `/blog/[id]/page.tsx` or `/posts/[slug]/page.js`.
+
+You can also feel free to skip ahead to the implementation below. This is what I used to get a working sitemap that
+included all the routes I cared about and, more importantly, was accepted by Google Search's console / robots:
+
+
+
+## Key things to understand
+
+Here's the things I needed to know upfront when I wanted to build my own sitemap:
+
+### The Next.js file to route convention is supported
+
+You can use the file-convention approach, meaning that a file that lives at `src/app/sitemap.(js|ts)`
+
+Your file should export a default function - conventionally named `sitemap`. I've demonstrated this in the
+implementation example below.
+
+### You can test your sitemap locally
+To test your sitemap, you can use curl like `curl http://localhost:3000/sitemap.xml > /tmp/sitemap.xml` and I recommend redirecting
+the output to a file as shown so that you can examine it more closely.
+
+At a minimum, all of the URLs you want search engines and other readers of sitemaps to be aware of need to be present in this file
+and wrapped in `` or location tags.
+
+If your sitemap is missing key URLs you're looking to get indexed, there's something wrong in your logic causing your routes to be excluded.
+
+You can do print debugging and add logging statments throughout you sitemap file so that when you're running `npm run dev` and you curl
+your sitemap, you can see the output in your terminal to help you diagnose what's going on.
+
+## Next.js dynamic and static sitemap.xml example
+
+The following implementation creates a sitemap out of [my portfolio project](https://github.com/zackproser/portfolio).
+
+It works by defining the root directory of the project (`src/app`), and then reading all the directories it finds there, applying the following rules:
+
+1. If the sub-directory is named `blog` or `videos`, it is a dynamic route, which needs to be recursively read for individual posts
+2. If the sub-directory is not named `blog` or `videos`, it is a static or top-level route, and processing can stop after obtaining the entry name. This will be the case
+for any top-level pages such as `/photos` or `/subscribe`, for example.
+
+Note that if you're copy-pasting this into your own file, you need to update:
+1. your `baseUrl` variable, since it's currently pointing to my domain
+2. the names of your dynamic directories. For my current site version, I only have `/blog` and `/videos` set up as dynamic routes
+3. Your excludeDirs. I added this because I didn't want my API routes that support backend functionality in my sitemap
+
+```javascript
+import fs from 'fs';
+import path from 'path';
+
+const baseUrl = process.env.SITE_URL || 'https://zackproser.com';
+const baseDir = 'src/app';
+const dynamicDirs = ['blog', 'videos']
+const excludeDirs = ['api'];
+
+function getRoutes() {
+ const fullPath = path.join(process.cwd(), baseDir);
+ const entries = fs.readdirSync(fullPath, { withFileTypes: true });
+ let routes = [];
+
+ entries.forEach(entry => {
+ if (entry.isDirectory() && !excludeDirs.includes(entry.name)) {
+ // Handle 'static' routes at the baseDir
+ routes.push(`/${entry.name}`);
+
+ // Handle dynamic routes
+ if (dynamicDirs.includes(entry.name)) {
+ const subDir = path.join(fullPath, entry.name);
+ const subEntries = fs.readdirSync(subDir, { withFileTypes: true });
+
+ subEntries.forEach(subEntry => {
+ if (subEntry.isDirectory()) {
+ routes.push(`/${entry.name}/${subEntry.name}`);
+ }
+ });
+ }
+ }
+ });
+
+ return routes.map(route => ({
+ url: `${baseUrl}${route}`,
+ lastModified: new Date(),
+ changeFrequency: 'weekly',
+ priority: 1.0,
+ }));
+}
+
+function sitemap() {
+ return getRoutes();
+}
+
+export default sitemap;
+```
+
+That's it! Save this code to your `src/app/sitemap.js` file and test it as described above.
+
+I hope this was helpful, and if it was, please consider
+subscribing to my newsletter below! 🙇
diff --git a/src/images/next-js-sitemap.webp b/src/images/next-js-sitemap.webp
new file mode 100644
index 00000000..fe503156
Binary files /dev/null and b/src/images/next-js-sitemap.webp differ
diff --git a/src/images/sitemap-google-search.webp b/src/images/sitemap-google-search.webp
new file mode 100644
index 00000000..8734f865
Binary files /dev/null and b/src/images/sitemap-google-search.webp differ