Build a blog with AstroJS using MDX Integration
Introduction
In this tutorial, readers will learn about Astro, its features, and why they should consider using Astro. They will also learn how to build an application with AstroJS by working on a blog app with MDX integration. MDX is a Markdown extension that allows the user include JSX along with the Markdown. Markdown is a writing style that is a widely used and acceptable pattern, as it is quickly learned and allows both technical and non-technical parties to create and manage content. Unlike WYSIWYG editors, Markdown will enable users to apply text formatting and insert media elements with simple symbols and writing patterns.
What is Astro
Astro is a JavaScript framework for creating static multi-page web applications, which minimizes the use of JavaScript, boosts the speed and loading time of static websites, and adds JavaScript when necessary. It was built specifically for content-driven web applications and did not aim to compete with other popular frameworks; instead, it can be used as a tool and integrated to work together with other frameworks. Other such libraries for creating static web applications, that can be integrated with popular frameworks are: Jekyll, Gatsby, MiddleMan and Mkdocs.
Why use Astro
Below are the reasons why AstroJS is a good choice for your next project:
- Easy learning curve: AstroJS uses HTML, CSS, JavaScript and your knowledge of other existing frameworks. There is no need to learn a new syntax or coding pattern, so it is easy to learn and integrate for use.
- AstroJS creates zero-JavaScript static web applications, rendering them to static HTML at build-time. This makes web pages load faster and more seamlessly. Allowing the user to use client-side JavaScript when required.
Prerequisites
To work with Astro, you will need to meet the following criteria:
- Have a working version of Node.js installed on your computer
- Be knowledgeable in HTML, CSS, and JavaScript.
Project Setup
To get started with AstroJS, you must create an Astro project using the CLI. On your local machine, navigate to a directory of your choice and enter the following command in a CLI:
npm create astro@latest
Follow the prompts to create your Astro application. This tutorial will use the basic template with a simple TypeScript setup. Once your installation is complete, open up your application in your choice code editor.
To add MDX support to our Astro application, we will install the following dependencies
npx astro add mdx
To preview the current state of your application, run npm run dev
in the CLI and open up the localhost URL in your browser. The result you will get is similar to the image below:
Creating App Components
For our application, we will create two primary components, a hero and a card component. To do this, create two files in the components
folder: Hero.astro
and BlogCards.astro
.
In Hero.astro
, add the following code:
--- export interface Props { welcome: string; body: string; } const { welcome, body } = Astro.props; --- <div class="hero"> <h2>{welcome}</h2> <p>{body}. <strong>Dive in!</strong></p> </div>
In the code above, we have a basic Astro layout. Astro uses a superset of JavaScript, unlike other popular frameworks such as React, which uses JSX. JavaScript and TypeScript are enclosed in triple dashes (---), marking the start and end of the code blocks. Here, we have defined two props welcome
and body
of type String. We will pass the values of these props from our index
file and display them as desired.
To create the card components for our blog content, add the following code to BlogCard.astro
:
--- export interface Props { title: string; summary: string; } const { title, summary } = Astro.props; --- <li class="link-card"> <a href="#"> <h2> {title} <span>→</span> </h2> <p>{summary}</p> </a> </li>
Here, we have the cards, displaying the title and summary of our content as props.
Creating App Layout
As the name layout
implies, this component will wrap around other pages. It can be used to prevent repetitive coding of content that is to be available on different pages, such as navigation bars, headers, footers, etc. In the layouts
folder, edit Layout.astro
as shown below:
--- export interface Props { title: string; } const { title } = Astro.props; --- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="generator" content={Astro.generator} /> <title>{title}</title> </head> <body> <div class="navbar"> <!-- header and navigation --> <p class="nav-text">My Astro Blog</p> <div class="return"> <a href="/">Return Home</a> <span>→</span> </div> </div> <slot /> <footer class="footer"> <!-- footer --> <p>Thank you for visiting</p> </footer> </body>
In the code block above, we have our page's navigation bar and footer. We are also using a title
prop for our page.
Creating App MDX Page
In this section, we will create the landing page for our blog application. In index.astro
add the following code:
--- import Layout from "../layouts/Layout.astro"; import BlogCard from "../components/BlogCard.astro"; import Hero from "../components/Hero.astro"; --- <Layout title="Welcome to Astro."> <main> <div class="hero-cont"> <Hero welcome="Welcome to my Astro blog." body="A fast and scalable blog covering numerous topics and thoughts." /> </div> <p class="prompt-text"> <code>Code</code> and <code>Techinal</code> articles. Have a look<br /> <strong>Look through all our amazing content...</strong> </p> <ul role="list" class="link-card-grid"> <!-- Blog cards will be displayed here --> </ul> </main> </Layout>
In the code block above, we added the layout
, hero
and blog card
components. We will add some mdx files, and display the cards on the page in the space provided. In the src
directory, create a folder called posts
. In this folder, create three files, example1.mdx
, example2.mdx
, and example3.mdx
, respectively. In these files, add the following:
--- title: This is My First Article Post summary: A summary is a short piece of information, spanning all your write-up aims to pass across, or teach prospective readers slugUrl: first-article-slug-url --- import "./postStyles.css"; <div class="title"> <h1>{frontmatter.title}</h1> </div> <div> # A H1 title > Some text with a blockquote ## A H2 heading example here This is a _bold text_. Bullet list below: - Bullet one - Bullet two - Bullet three - Sub bullet here - another sub category </div> ```js const sayHello()
Since we are working with MDX
we can make use of JSX and Stylesheets in our file alongside Markdown. In the posts
directory create a new file postStyles.css
and add the following styles:
.title { font-size: 1.6rem; font-weight: 700; padding: 5rem 0; margin-bottom: 3rem; text-align: center; background: #ddd; }
To differentiate the posts, we will modify the title and slug of the example1.mdx
and example2.mdx
as shown below:
---- // for example2.mdx title: This is my Second Article summary: A summary is a short piece of information, spanning all your write-up aims to pass across, or teach prospective readers slugUrl: second-article-slug-url --- --- // for example3.mdx title: This is my Third Article summary: A summary is a short piece of information, spanning all your write-up aims to pass across, or teach prospective readers slugUrl: third-article-slug-url ---
Since we are working with MDX
we can also create and import a custom component. We will create and add a custom component for an information box. In the posts
directory, create a new file InfoBox.astro
. In this file, we will create the Information component as follows:
--- export interface Props { information: {}; } const { information } = Astro.props; --- <div class="info-cont"> <p class="info-text">{information}</p> </div>
We can add this component in our mdx files as follows:
//import for InfoBox container import InfoBox from "./InfoBox.astro";
Then pass the information to the InfoBox
container as a prop:
<InfoBox information={"This is some important information that you should know"} > </InfoBox>
Next, we will fetch all our posts from our index.astro
file:
const post = await Astro.glob("../posts/*mdx")
Here, we are fetching all the files from the posts
directory with a .mdx
file extension. We can then display the cards, using the BlogCard
component, in our index.astro
file as shown below:
<ul role="list" class="link-card-grid"> <!-- Blog cards will be displayed here --> {post.map((post) => { return ( <BlogCard title={post.frontmatter.title} summary={post.frontmatter.summary} /> ); })} </ul>
In the code above, we are mapping through the data returned to the post
variable; our markdown files, and we are passing each file’s title and summary as props
to the BlogCard
component to display it.
Adding Routes
For each card in our blog posts, we will add an anchor tag containing the selected post's title. After this, in the next section, we will create the page to display the blog post. In the BlogCard
component, make the following changes:
export interface Props { title: string; summary: string; // add a new prop for the URL slugUrl: string; } const { title, summary, slugUrl } = Astro.props;
Next, we pass this prop
to the anchor element href
property:
<!-- previous code --> <a href={`/${slugUrl}`}> <h2> {title} <span>→</span> </h2> <p>{summary}</p> </a>
With this done, we need to pass the URL prop
of the selected card to the BlogCard
component. We will do this in index.astro
:
// previous code in map function return ( <BlogCard title={post.frontmatter.title} summary={post.frontmatter.summary} slugUrl={post.frontmatter.slugUrl} /> );
Rendering Blog Contents
Finally, to render our blog content, we will create and style a page that will display the content of the selected post. Create a new file [slugUrl].astro
in the pages directory. Like Next.js, Astro treats all files in the pages
folder as a different application page and automatically creates routes. In this file, add the following code:
--- import Layout from "../layouts/Layout.astro"; console.log("hello"); export async function getStaticPaths() { const posts = await Astro.glob("../posts/*.mdx"); return posts.map((post) => ({ params: { slugUrl: post.frontmatter.slugUrl, }, props: { post, }, })); } // get article content const { Content, frontmatter } = Astro.props.post; console.log(Content); --- <> <Layout title={frontmatter.title}> <article> <div class="content"> <!-- article content --> <Content /> </div> </article> </Layout> </> <style> .content { max-width: 800px; margin: 0 auto; } </style>
Here, we are making use of getStaticPath
to check the slugUrl and return the appropriate content.
Final Results
Congratulations on completing this tutorial. Now, if we run our application with the npm run dev
command, we will get the following results:
NB: All styles used in this tutorial can be found in the following GitHub repo.
Conclusion
We have come to the end of this tutorial. In this tutorial, we learned about AstroJS and its features. Then, we demonstrated how AstroJS could be used by working on a real-world blog application project.