--- title: Welcome to my blog! date: 2023-10-15 summary: How I built my blog cowsay: Hello World! --- import CowSay from "@components/blog/CowSay.astro"; ## Hey there I decided to make this blog as a way to track my progress in learning new things. I hope you enjoy your stay! This first post is going into a bit of detail about how I made this blog. ## Making The Basic Blog Astro has a wonderful feature called the [content framework](https://docs.astro.build/en/guides/content-collections/), an extremely powerful way to easily create many pages with simple markdown and some frontmatter. First thing you have to to do is create a folder called `content` in the src directory. I already had some content setup because of the projects parts of this site. Inside the content folder you place a `config.ts` which will contain the schemas for your content's frontmatter. I'll just focus on my blog posts for now. ```ts const blogPostsCollection = defineCollection({ schema: z.object({ title: z.string(), date: z.date(), summary: z.string(), cowsay: z.string() }) }); ``` This contains the metadata each blog post will need to have in order for my site to render it. Then, we export an object named `collections` which Astro will pick up and generate TS bindings for. ```ts export const collections = { posts: blogPostsCollection }; ``` Now we can get to writing some content! Make a folder with the same name as the _key_ of the collection you want to write for. In this case, `posts`. Then create a markdown file and start writing! Here's a little excerpt of what [this page looks like](https://github.com/Bwc9876/portfolio-site/tree/main/src/content/posts/hello_world.mdx): ```md --- title: Welcome to my blog! date: 2023-10-15 summary: How I built my blog. cowsay: Hello World! --- ## Hey there ``` The frontmatter is the part between the `---` and `---`. This is where you put metadata for the post. The `cowsay` field is a special one that I made up. It changes what the cow says in the header of the page. I'll get to that later. Now that we have some content, we can start writing some code to render it! I start off by making a `blog` folder in the `src/pages` directory. This is where all my blog related pages will go. I then make a `index.astro` file which will be a directory of all posts on the site. ```astro --- import Layout from "@layouts/Layout.astro"; import { getCollection } from "astro:content"; const blogEntries = await getCollection("posts"); ---

The Cowsay - Ben C's Blog

Here you'll find my blog posts, most recent first

{ blogEntries.map((p, i) => ( <> {i === 0 &&
}

{p.data.title}

{p.data.date.toLocaleDateString("en-us", { weekday: "long", year: "numeric", month: "short", day: "numeric" })}

{p.data.summary}  Read More


)) }
``` Great! I'll probably fiddle with it in the future but it's a good start. Now we need to make a page for each post. To make my URLs look nice I'm going to create a subfolder within `blog` called `posts` and then place a `[...id].astro` in there. This will allow me to use `getStaticPaths()` to define the paths for each post. ```astro --- import Layout from "@layouts/Layout.astro"; import { CollectionEntry, getCollection } from "astro:content"; export const getStaticPaths = async () => { const posts = await getCollection("posts"); return posts.map((entry) => ({ params: { slug: entry.id }, props: { entry } })); }; const { entry } = Astro.props as { entry: CollectionEntry<"posts"> }; const { Content } = await entry.render(); ---

{entry.data.title}

``` Amazing! I'll spare you the image this time since... you're... look at it. But now we have a blog post page! Now anytime I want to make a new post I just have to make a new markdown file and it'll be rendered on the site. ## An Outline Now that we have a basic blog, I want to add a few more features to it. First I want to add the ability to see all headers in a post. This should be pretty easy to do. Astro automatically parses all the headers for us and lets us access them in the `entry` object. First we need to grab the headings from when we rendered the page: ```js const { Content, headings } = await entry.render(); ``` Then we need to map those to HTML ```astro
On This Page
``` Finally some simple styles and layout ```astro ``` Finally, I want to make sure on smaller screen sizes the table of contents appears at the top rather than the side so it doesn't take up too much space. ```css div.wrapper { display: flex; flex-direction: column-reverse; gap: 1rem; } @media (min-width: 1200px) { div.wrapper { flex-direction: row; gap: 4rem; } } ``` ## The Cowsay Now for the fun part. I want to make a little cow that says something in the header of each post. I'm going to use the `cowsay` field in the frontmatter to do this. I'll also provide a CowSay component that will render the cow and the text. This way I can use it MDX for admonitions. First I need to make a component that will render the cow. I'm going to use the [cowsay](https://www.npmjs.com/package/cowsay) package to do this. ```astro --- import * as cowsay from "cowsay"; type Props = { color?: "warn" | "info"; } & cowsay.IOptions; const { color, ...cowOptions } = Astro.props; const cowText = cowsay.say(cowOptions); ---
{cowText}
``` Now I can link it up in my blog post page! ```astro ``` Et voila! A cow that says something in the header of each post! I'll probably make it a bit more fancy in the future but for now it's good enough. I can also use it for admonitions in MDX! ```astro ``` ## Conclusion I'm really happy with how this blog turned out. I'm going to keep working on it and adding new features as I go. I'm also going to try and write more posts in the future. I hope you enjoyed this one!