Sitemap, RSS Feed, & Static Routes with Next.js App Router

Sitemap, RSS Feed, & Static Routes with Next.js App Router


Sitemap, RSS Feed, & Static Routes with Next.js App Router

Generate sitemaps, RSS feeds, and other static files like search indexes easily with the Next.js App Router.

Learn how to use the Sitemap route to build your sitemap.xml and use static route handlers to generate other static files like feeds and JSON files.

Sign up for my newsletter: https://www.colbyfayock.com/newsletter/

---

# Resources

Tutorial: https://spacejelly.dev/posts/sitemaps

Static Route Handlers: https://nextjs.org/docs/app/building-
Sitemap: https://github.com/colbyfayock/next-w
RSS Feed: https://github.com/colbyfayock/next-w
Search JSON: https://github.com/colbyfayock/next-w

https://twitter.com/colbyfayock
https://twitch.tv/colbyfayock
https://www.colbyfayock.com/uses

#colbyfayock #nextjs #sitemap #rss #nextjs13 #nextjstutorial #webdevelopment


Content

0.06 -> site maps are just as important as ever
2.159 -> to make sure that you're communicating
3.6 -> your website's content to the web and
5.58 -> Long Live RSS feeds there's still a lot
7.56 -> of valid use cases Beyond feed readers
9.66 -> whether using zapier or pipe dream to
12.24 -> automate some of your workflows or maybe
13.679 -> you're using it programmatically inside
15.059 -> of your applications but regardless of
17.279 -> the use case next.js traditionally
19.02 -> didn't really make it easy to be able to
20.76 -> provide this capability where you had to
22.56 -> use some kind of hacky workflow to get
24.06 -> there but that's all changed with the
25.5 -> nexjs app router which gives us a lot of
27.359 -> capabilities for generating those static
29.16 -> files programmatically so let's see how
31.14 -> we can build our site map and other
32.46 -> static files like RSS feeds inside an
34.38 -> xjs
36.5 -> to see how this works I'm going to be
38.52 -> working on my next.js WordPress starter
40.32 -> which I've begun migrating over to the
41.879 -> app router one of the things that I had
43.5 -> to do in the previous iteration is
45.239 -> manually generate this file using all my
47.579 -> site content to make this work I kind of
49.62 -> set up a webpack plugin where it's just
51.42 -> the mechanism that triggers the event so
53.64 -> that it actually grabs all the data and
55.26 -> compiles out that XML file I had it
57.48 -> hooked into the next config.js file
59.46 -> along with some other things including
61.14 -> that RSS feed as well as an index for my
63.899 -> search but now I want to recreate it
65.519 -> inside of the nexjs app router and it's
67.26 -> so much easier to build that sitemap
68.939 -> file with a new API now I don't want to
71.22 -> statically include all these different
72.6 -> files so what I want to do is generate
74.64 -> that file and the way I can set that up
76.439 -> is by starting adding my sitemap.js or
79.2 -> TS if you're using typescript right
81.119 -> inside of that app directory so inside
82.68 -> of my app directory I'm going to create
84.18 -> that new file of sitemap.js but the
87 -> first thing I'm going to do is export a
88.619 -> default async function called sitemap
91.5 -> and ultimately I'm going to return an
93.42 -> array that's going to include all the
95.1 -> different pages I want to include now
96.72 -> that syntax is going to be my object
98.4 -> where I include my URL where I can paste
100.92 -> in my next.js WordPress starter which
102.659 -> ideally I could get dynamically but
104.1 -> let's just leave it out there for now
105.299 -> but then I want to also include a last
107.28 -> modified which is going to be the the
109.619 -> last time that that content was updated
111.54 -> and we can just use something for the
113.22 -> home page like new date but now let's
115.92 -> check up our progress on this we're
117.479 -> literally all we're doing right now is
118.799 -> defining a default async function of
120.72 -> sitemap and we're returning an array
122.52 -> with our home page if I now go to
124.38 -> localhost 3000
125.96 -> sitemap.xml we can see that I now just
128.819 -> as simple as that have my new site map
130.56 -> now the nice thing about defining this
132.18 -> as an async function is now I can
133.86 -> actually make requests inside so I'm
135.599 -> going to import this function where I
137.04 -> can run get all posts and get them all
139.08 -> inside of an array now once I have that
141 -> request being made and I have access to
142.98 -> all those posts all I need to do now is
145.2 -> actually format that where I can create
146.7 -> my constant of posts where I can map
148.68 -> through all of my posts and for each of
150.72 -> those posts I ultimately want to return
152.58 -> that URL in the last modified where in
155.76 -> my case I I want to set this up and I'm
157.379 -> going to use the template literal tag so
158.819 -> that I can make this actually Dynamic
160.319 -> but it's going to be my
162.08 -> posts.uri it looks like I actually need
164.28 -> to make that post not post to make sure
166.44 -> it's that single instance and my last
168.36 -> modified date is actually already going
169.92 -> to be available under post.modified now
172.739 -> it's probably best to make sure that
173.819 -> you're including a full URL for the
175.98 -> actual URL in my case I'm going to reach
178.26 -> out to Wordpress using WP graphql and
180.72 -> actually get that URL that's going to be
183.239 -> the public facing site so inst in front
185.76 -> of my URI I'm also going to include
188.239 -> metadata.url which will give me that
190.26 -> fully formatted URL But ultimately with
192.42 -> that formatted I can now just simply
194.34 -> spread out
195.48 -> all those posts into my returned array
198 -> and once I refresh the page we can see
199.739 -> that I have not only my home page but I
201.959 -> have all those posts being dynamically
203.819 -> pulled in from my existing functions
206.4 -> that request that data for me now the
208.56 -> sitemap having first class support makes
210.42 -> it really nice and easy for being able
212.819 -> to just simply dump all this content
214.319 -> inside an XML file make sure that
216.54 -> next.js is handling all the cache for us
218.28 -> and there's really not much more we need
219.9 -> to do beyond that but when it comes to
221.58 -> the RSS feeds there were other types of
223.26 -> feeds there's not a first class way like
224.94 -> site maps to do that but instead we can
227.22 -> use static route handlers which will
229.14 -> allow us to really build whatever file
231.06 -> we want in that static way now to do
233.64 -> this the way we need to set it up is
235.08 -> under our app directory we want to
237.12 -> define the route of where we want that
238.799 -> to be and then include our route.js or
241.62 -> TS file inside which will include that
244.14 -> logic including our get request for
246.599 -> defining that file output so back inside
248.76 -> of my app directory I'm going to create
250.319 -> a new directory called
252.08 -> feed.xml and inside that folder I'm
254.519 -> going to create a new file
255.979 -> route.js inside I'm going to export an
259.139 -> async function called gets where inside
261.66 -> I'm ultimately going to do the same
263.22 -> thing I did for my sitemap but just be a
265.62 -> little bit more manual for how I handle
267.6 -> the response so I'm going to actually
268.979 -> copy a lot of this over where I'm going
270.419 -> to get my metadata and my all the posts
272.58 -> But ultimately the way that we respond
274.56 -> to this request is going to be different
276.3 -> than how we responded with an array with
278.1 -> our site map first of all we need to
279.9 -> actually create an RSS feed or manually
282.36 -> create that XML to return in that
284.46 -> response now technically we could
286.139 -> probably do that all with some template
287.82 -> literal tags where we actually stuff our
289.56 -> data inside but that's a little bit
291.3 -> messy in my opinion to actually have to
292.919 -> manage so instead I'm going to use an
294.9 -> npm library to handle that particularly
297 -> I'm going to use node RSS which is a
298.919 -> little bit old but it does a really good
300.54 -> job for being able to easily set up a
302.34 -> new RSS feed so inside my terminal I'm
304.32 -> going to run npm install RSS and inside
306.78 -> of my project I'm going to import RSS
309 -> from the RSS package and then I'm going
311.22 -> to start off by creating a constant of
312.66 -> feed and set that equal to a new
314.04 -> instance of RSS we're inside I'm going
316.8 -> to start setting up all the different
317.82 -> fields that I want to include in the RSS
319.5 -> including my title of course which in my
321.78 -> instance I'm able to easily get from my
323.34 -> site metadata so I'm going to include my
325.28 -> metadata.title as well as my description
328.02 -> which are actually coming directly from
330.12 -> WordPress and then I'm going to copy and
331.979 -> paste a few more fields in there
333.3 -> including my set URL feed URL a
335.4 -> copyright statement the language which
337.62 -> again is coming from WordPress as well
339.479 -> as a pup date which is joined to be just
341.58 -> when this actual RSS feed was published
343.68 -> which in this case I'm just going to use
345.12 -> new date but now before we start adding
346.979 -> our posts let's just double check that
348.539 -> this is working so the way that this is
350.28 -> going to work is we're going to return a
352.259 -> new response where we're going to
354.06 -> ultimately pass through our content but
355.86 -> we want to also set up a response or
358.02 -> because we're creating an RSS feed we
359.58 -> want to make sure we're setting up the
360.66 -> right content headers so I'm going to
362.28 -> paste this in which I'm going to set my
363.72 -> content type of application atom XML
366.3 -> with my care set of utf-8 but the
368.88 -> important part is this actual content
370.38 -> and the way that we can do this is by
372.12 -> passing through our feed but we're going
374.22 -> to chain on the XML method so now if we
376.8 -> go to the browser and instead try to
378.36 -> load feed.xml we can see that we now get
381.6 -> this RSS feed easily generated with all
384 -> these details that we added inside so
385.8 -> now let's actually start adding all of
387.24 -> our posts for every post that we have we
389.52 -> want to ultimately use the feed dot item
391.68 -> method so the way that I'm going to
393 -> handle that is I'm going to run all
394.319 -> posts for each post I'm going to
397.38 -> ultimately run that feed dot item and
399.72 -> just like the top level RSS feed itself
401.46 -> we're going to have a few things that we
402.78 -> need to pass in including the title
404.46 -> which I can get from post.title but I'm
407.1 -> going to paste in the rest of my
408.3 -> specific Fields where we have a lot of
409.86 -> important fields that we want to make
411 -> sure we're including here including our
412.919 -> guide our URL our date you can even
415.199 -> include a description in the categories
417 -> but we want to make sure that we have
418.5 -> some of these top level important ones
420.3 -> so that anything that's trying to read
422.1 -> our feed is able to do it in a valid way
424.08 -> while you're building this if you want
425.52 -> to make sure that your feet is valid
427.02 -> after you deploy it because it needs to
428.88 -> be publicly available or if you use
430.68 -> direct input you can use this w3c feed
433.199 -> validation service which you can find by
434.88 -> just simply Googling for RSS feed
437.4 -> validator or I'll include the link in
439.08 -> the description but now let's actually
440.46 -> test this out where if I save it and
442.56 -> reload the page we can see that we get
444.24 -> all that content in including having an
446.46 -> item for each and every one of those
447.84 -> posts but if I even wanted to check to
449.699 -> make sure that this is valid I can go
451.319 -> ahead and copy The Source from this head
453.66 -> over to that service go to direct input
455.759 -> and paste it in and make sure that I
457.62 -> remove any white space in front of it
459.24 -> then I click check and we can see that
461.699 -> it is successfully valid the nice thing
463.86 -> is we can really extend this static
465.36 -> route handling to any situation we want
467.099 -> whether it is our feeds or maybe if we
469.199 -> wanted to create an index of all of our
470.88 -> posts and create some kind of easy to
472.74 -> use search index we can create a
474.78 -> search.json file include our route.js we
477.9 -> can grab all those posts and format it
479.639 -> with only the data that we want and we
481.44 -> can even use the next response helper
483.96 -> here from next server including that
485.94 -> Json method to pass back our data for
488.34 -> now if I instead of feed.xml go to
490.46 -> search.json we can see I have all the
493.08 -> data I need to be consumed however I
495.24 -> want the nexjs app router has given us a
497.52 -> lot of apis to create flexible ways for
499.68 -> building our data have you started to
501.479 -> dig in and migrate your app yet let me
503.34 -> know what you think in the comments if
504.78 -> you want to make sure that your site
505.8 -> remains Pixel Perfect as you're
507.18 -> migrating over to the app router check
508.979 -> out my video where we learn how to use
510.18 -> visual testing with apple tools if you
512.159 -> like this video make sure you hit thumbs
513.419 -> up subscribe and click that little
514.56 -> notification Bell for future web dev
516 -> content thanks for watching

Source: https://www.youtube.com/watch?v=6wD3AXZy71M