With the release of Astro 2.0 , the Content Collections API became available.
This makes it very easy to query Markdown/MDX in a type-safe way (and more, be sure to read the blog post I linked above).
For my blog it was really easy to set up, I just had to move my blog from to /pages/blog
and /content/blog
add a /content/config.ts
file.
This /content
folder is a reserved folder for the repository.
In /content/config.ts
the file, you define the schema for the collection you want to create.
Currently I only have 1 collection which is my blog.
So I created my schema as follows:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">z</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">defineCollection</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">astro:content</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">blog</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">defineCollection</span><span style="color:var(--syntax-text-color)">({</span>
<span style="color:var(--syntax-name-color)">schema</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">z</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">object</span><span style="color:var(--syntax-text-color)">({</span>
<span style="color:var(--syntax-name-color)">title</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">z</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">(),</span>
<span style="color:var(--syntax-name-color)">layout</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">z</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">(),</span>
<span style="color:var(--syntax-name-color)">tags</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">z</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">array</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">z</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">()),</span>
<span style="color:var(--syntax-name-color)">date</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">z</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">(),</span>
<span style="color:var(--syntax-name-color)">image</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">z</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">().</span><span style="color:var(--syntax-name-color)">optional</span><span style="color:var(--syntax-text-color)">(),</span>
<span style="color:var(--syntax-text-color)">}),</span>
<span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-declaration-color)">export</span> <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">collections</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">blog</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">};</span>
</code></span></span>
As you can see, tags
is a required attribute for all my blogs, luckily I have tagged every blog post from the beginning.
In my MDX, the tags are defined at the top of the file as follows:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>---
tags: ['javascript', 'edge', 'vercel'];
---
</code></span></span>
.astro
Now that I have the Blogs collection defined in my config, I can start using the data from my files.
I want to create a separate page for each unique tag used in the blog.
To make this possible, I just added a file /pages/blog/tags/[...slug].astro
(it's a dynamic route ).
In this file, I want to fetch all blogs from my collection, find unique tags, and generate a URL for each tag.
The getStaticPaths()
function will return the URL (hence the line) that I want to pre-render during build export const prerender = true;
.
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-error-color)">---</span>
<span style="color:var(--syntax-declaration-color)">export</span> <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">prerender</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">true</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">slug</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">Astro</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">params</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">getCollection</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">astro:content</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">blogs</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">getCollection</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">blog</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">tags</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">blogs</span>
<span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">map</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">blog</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-name-color)">blog</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">data</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">tags</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">flat</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">reduce</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-name-color)">key</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">string</span><span style="color:var(--syntax-text-color)">]:</span> <span style="color:var(--syntax-name-color)">number</span> <span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-error-color)">></span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">function</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">result</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">c</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-name-color)">count</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">result</span><span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-name-color)">c</span><span style="color:var(--syntax-text-color)">]</span> <span style="color:var(--syntax-error-color)">||</span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-name-color)">result</span><span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-name-color)">c</span><span style="color:var(--syntax-text-color)">]</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">count</span> <span style="color:var(--syntax-error-color)">+</span> <span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">result</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">},</span> <span style="color:var(--syntax-text-color)">{});</span>
<span style="color:var(--syntax-declaration-color)">export</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-declaration-color)">function</span> <span style="color:var(--syntax-name-color)">getStaticPaths</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">blogs</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">getCollection</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">blog</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">[...</span><span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-text-color)">Set</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">blogs</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">map</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">blog</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-name-color)">blog</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">data</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">tags</span><span style="color:var(--syntax-text-color)">).</span><span style="color:var(--syntax-name-color)">flat</span><span style="color:var(--syntax-text-color)">())].</span><span style="color:var(--syntax-name-color)">map</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">tag</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">({</span>
<span style="color:var(--syntax-name-color)">params</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">slug</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">tag</span> <span style="color:var(--syntax-text-color)">},</span>
<span style="color:var(--syntax-text-color)">}));</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-name-color)">slug</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">Astro</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">redirect</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">/404</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">allBlogPostsWithTag</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">getCollection</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">blog</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">blog</span> <span style="color:var(--syntax-error-color)">=></span>
<span style="color:var(--syntax-name-color)">blog</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">data</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">tags</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">some</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">tag</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-name-color)">tag</span> <span style="color:var(--syntax-error-color)">===</span> <span style="color:var(--syntax-name-color)">slug</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">allBlogPostsWithTag</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">length</span> <span style="color:var(--syntax-error-color)">===</span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">Astro</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">redirect</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">/404</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-error-color)">---</span>
</code></span></span>
I used a .reduce()
function to count the number of blogs available per tab, so I could display it at the top of the tab.
The API provided by Content Collections are functions getCollection()
that allow me to get all the blogs in my collection blogs
. As you can see, it is also possible to pass a filter function to this function, I used to get only the blogs that contained the tag on line 22 for which the current page was generated. The actual rendering is simple, I can now
use allBlogPostsWithTag
variables and render blog posts.
Since slug
it matches the generated tags for this page, I can find the code in my title/description/..
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-name-color)">Layout</span>
<span style="color:var(--syntax-name-color)">title</span><span style="color:var(--syntax-text-color)">=</span><span style="color:var(--syntax-string-color)">{</span><span style="color:var(--syntax-string-color)">`The Thomas Ledoux blog | </span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-name-color)">slug</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)">`</span><span style="color:var(--syntax-string-color)">}</span>
<span style="color:var(--syntax-name-color)">description</span><span style="color:var(--syntax-text-color)">=</span><span style="color:var(--syntax-string-color)">{</span><span style="color:var(--syntax-string-color)">`All blogs about </span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-name-color)">slug</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)">`</span><span style="color:var(--syntax-string-color)">}</span>
<span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-error-color)">section</span><span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-error-color)">h1</span> <span style="color:var(--syntax-name-color)">class</span><span style="color:var(--syntax-text-color)">=</span><span style="color:var(--syntax-string-color)">"text-center text-2xl font-bold mb-6"</span><span style="color:var(--syntax-text-color)">></span>
All blogs about <span style="color:var(--syntax-string-color)">{</span><span style="color:var(--syntax-name-color)">slug</span><span style="color:var(--syntax-string-color)">}</span>
<span style="color:var(--syntax-text-color)"></</span><span style="color:var(--syntax-error-color)">h1</span><span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-name-color)">Tags</span> <span style="color:var(--syntax-name-color)">tags</span><span style="color:var(--syntax-text-color)">=</span><span style="color:var(--syntax-string-color)">{</span><span style="color:var(--syntax-name-color)">tags</span><span style="color:var(--syntax-string-color)">}</span> <span style="color:var(--syntax-name-color)">slug</span><span style="color:var(--syntax-text-color)">=</span><span style="color:var(--syntax-string-color)">{</span><span style="color:var(--syntax-name-color)">slug</span><span style="color:var(--syntax-string-color)">}</span> <span style="color:var(--syntax-text-color)">/></span>
<span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-error-color)">div</span>
<span style="color:var(--syntax-name-color)">class</span><span style="color:var(--syntax-text-color)">=</span><span style="color:var(--syntax-string-color)">"grid sm:grid-cols-2 lg:grid-cols-3 gap-6 items-center auto-rows-min mt-4"</span>
<span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-string-color)">{</span>
<span style="color:var(--syntax-name-color)">allBlogPostsWithTag</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">map</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">post</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">i</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">remarkPluginFrontmatter</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">post</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">render</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">(</span>
<span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-name-color)">PostPreview</span>
<span style="color:var(--syntax-name-color)">minutesRead</span><span style="color:var(--syntax-text-color)">=</span><span style="color:var(--syntax-string-color)">{</span><span style="color:var(--syntax-name-color)">remarkPluginFrontmatter</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">minutesRead</span><span style="color:var(--syntax-string-color)">}</span>
<span style="color:var(--syntax-name-color)">post</span><span style="color:var(--syntax-text-color)">=</span><span style="color:var(--syntax-string-color)">{</span><span style="color:var(--syntax-name-color)">post</span><span style="color:var(--syntax-string-color)">}</span>
<span style="color:var(--syntax-name-color)">index</span><span style="color:var(--syntax-text-color)">=</span><span style="color:var(--syntax-string-color)">{</span><span style="color:var(--syntax-name-color)">i</span><span style="color:var(--syntax-string-color)">}</span>
<span style="color:var(--syntax-text-color)">/></span>
<span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">})</span>
<span style="color:var(--syntax-string-color)">}</span>
<span style="color:var(--syntax-text-color)"></</span><span style="color:var(--syntax-error-color)">div</span><span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-text-color)"></</span><span style="color:var(--syntax-error-color)">section</span><span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-text-color)"></</span><span style="color:var(--syntax-name-color)">Layout</span><span style="color:var(--syntax-text-color)">></span>
</code></span></span>
The result of this markup page will look like this:
I must say I really like writing Markdown/MDX with Astro, especially when they make it so easy! Source code can
be found on Github. An example of a tab page can be found here .