Markdown is a language that is widely used by developers to write different types of content, like posts, blogs, and documents. If you haven’t used it, chances are you’ve already seen it in action. For example, the README.md
file in GitHub and npm are written in Markdown.
Markdown provides an easy syntax for writing and structuring content compared to regular HTML or other writing tools. For example, in Markdown we can say:
## Hello World! I am here to see [John Doe](https://johndoe.com)
But in HTML, the same text would be:
<h2>Hello World!</h2> <p>I am here to see <a href="https://johndoe.com">John Doe</a></p>
Writing in Markdown feels like writing plain text, with a tiny bit of details to structure your content. This is why Markdown is mostly used in writing documentation and developers writing blog posts.
But, there are limitations to Markdown, one of which is writing interactive content.
Oftentimes in our articles or docs, we may want to add an interactive one-off widget to demonstrate something to the reader, but we can’t in Markdown.
However, MDX is a language that makes this possible in Markdown because it allows you to use JSX in Markdown.
So, in this article, we will look at how we can use this tool to build this blog in Next.js with no Gatsby, no Strapi, all from scratch.
What is MDX?
Before we jump into building this blog, let’s talk a little about what MDX is and what we can do with it.
It is necessary to be familiar with Markdown and JSX before working with MDX because MDX is simply a combination of Markdown and JSX.
MDX syntax
Because MDX allows you to use JSX in Markdown, you can use Markdown dynamically and interactively, like importing components into your Markdown or writing XML-like syntax in your markdown:
import Head from 'next/head'; <Head> <title>Hello World - My Blog</title> </Head> # Hello World
In the example above, we import a component Head from Next.js and use it to specify a title for our article. You can also notice the Hello World message with an # (which is an h1 in markdown). This is Markdown and JSX working together.
MDX doesn’t have a special or new set of syntax, it’s just a combination of Markdown and JSX in one place. Also, it is possible to save an MDX file with the .mdx
extension.
Expressions in MDX
The newest release (v2) of MDX makes it possible to write expressions in MDX, just like you would in JSX. A simple example is the following:
# Hello world I think 2 + 2 is {2+2}
This can be useful in docs or articles when you want to refer to a JavaScript variable, function expression, object, array, just name it.
ESM in MDX
MDX also supports importing and exporting. For instance, the BlogTitle
component below can be used as any other JSX component:
import Head from 'next/head'; export const BlogTitle = () => <h3>Hello World</h3> <Head> <title>Hello World - My Blog</title> </Head> # Hello World <BlogTitle />
Apart from components, noncomponents like objects, variables, and functions can be exported and imported:
import { year } from '../utils/date.js' # Top Ten Languages To Learn In {year}
Since we can use expressions in MDX, we can just pass in year
into our header easily. Think of any data you pass as expressions in JSX; now you can do it in your Markdown too:
import { posts } from '../posts/node.js' # A List of My NodeJS Posts <ul> {posts.map(post => <li>{post.title}</li>)} </ul>
It is also possible to create a variable in MDX like we saw with a functional component (BlogTitle
), but we need to prefix it with the export
keyword:
export const languages = ['Java', 'JavaScript', 'Python'] export const year = new Date().getFullYear(); # Top Ten Languages in {year} <ul> {languages.map(language => <li>{language}</li>)} </ul>
Prefixing every declaration with export
can be weird, but the good news is you don’t need to, you can simply declare it elsewhere and import it.
There is one exception where you may need to declare and use an object in your Markdown. That is when you use metadata:
export const meta = { title: "Your article doesn't beat this", publishedOn: "January, 6th. 2022", slug: "your-article-doesnt-beat-this" } # {meta.title}
While it’s not compulsory, it helps organize your content. You can also simply use Front Matter (which we’ll use in our blog later).
One interesting thing about MDX is that its files are compiled into JSX components. Their compilers (integrations from MDX) compile both MDX files (files in .mdx
) and Markdown files (files in .md
) into JSX components that can be used as any other components.
Say we have the example below saved as article.mdx
:
import Head from 'next/head'; export const BlogTitle = () => <h3>Hello World</h3> <Head> <title>Hello World - My Blog</title> </Head> # Hello World
We can import it as such:
import Article, { BlogTitle } from 'article.mdx'; const MyBlog = () => { <Article />; <BlogTitle />; }; export default MyBlog;
The article.mdx
file is compiled into a JSX component and exported by default, so we can import it into a .js/.jsx/.mdx
file as default. Also, recall that the BlogTitle
component was also exported from article.mdx.
Passing props
“If they can use it as any other JSX component, then they should also be able to pass props, right?” so I presume the MDX developers thought.
Well, they did it, and it is possible to pass data through props to any component, including the ones you create and the ones that compile to components by default.
We can refactor our last code to look like the following:
import Article, { BlogTitle } from 'article.mdx'; import { year } from '../utils/date.js'; const MyBlog = () => { <Article year={year} />; <BlogTitle />; }; export default MyBlog;
Our article.mdx
now looks like the following:
import Head from 'next/head'; export const BlogTitle = () => <h3>Hello World</h3> <Head> <title>Hello World - My Blog</title> </Head> # Hello World This year is {props.year}
Note that not all JSX syntax is allowed in MDX. There are exceptions between them, and you can find them on GitHub.
There are also extensions that provide language support like syntax highlighting for MDX for different text editors and IDEs. If you’re using VS Code, simply install MDX, and you’ll be good to go.
Enough with the introductions, let’s jump into code and see MDX in action.
Creating a blog with MDX
While creating this blog, we will do the following:
- Use a widget in one of the articles
- Display ten articles on the home page using Next.js
getStaticProps
. - Develop each article as an MDX file
- Use Front Matter for our article metadata
- Apply styles to our article content using only CSS
If you have never used Next.js before, you will still be able to follow along as long as you know React, Markdown, and JSX.
Configuring the Next.js app
Creating a Next.js app
Pardon me if this is obvious for you, but I want to try to carry everyone along.
To create your Next.js app, run the command below:
npx create-next-app@latest
You’ll be prompted to name your app, you can give yours any name, but for this tutorial, I will name the project “PressBlog”.
Installing dependencies
Let’s install all our dependencies here and now. To begin, run the command below:
npm install @mdx-js/loader@next @mdx-js/react@next gray-matter remark-frontmatter @reach/tooltip @reach/disclosure
You must use @next
to ensure you’re using the current version of mdx-js
. At the time of this writing, the current version is 2.0.0. Now, let’s review what each of these dependencies does.
@mdx-js/loader@next
@mdx-js/loader@next
is the integration that compiles Markdown and MDX into JavaScript. It is one of the integrations provided by MDX for webpack bundlers. We will configure it in our next.config.js
file.
@mdx-js/react@next
@mdx-js/react@next
provides the context for our app which we can wrap our components with and easily style our markdown contents.
gray-matter
In this blog, we will use Front Matter, and gray-matter will parse it into an object of metadata.
remark-frontmatter
MDX does not provide support for Front Matter, so we need a package to ignore Front MAtter when we view our MDX files as pages in the browser. We’ll use remark-frontmatter
for this.
@reach/tooltip
@reach/tooltip
provides a component we will use to create a tooltip in our blog article.
@reach/disclosure
@reach/disclosure
provides a component we will use for writing a disclosure in our blog article.
Configuring Next.js
To configure the Next.js app, copy the following into your next.config.js
:
import remarkFrontmatter from 'remark-frontmatter'; export default { webpack: (config, options) => { config.module.rules.push({ test: /\.mdx?$/, use: [ options.defaultLoaders.babel, { loader: '@mdx-js/loader', options: { providerImportSource: '@mdx-js/react', remarkPlugins: [remarkFrontmatter], }, }, ], }); return config; }, reactStrictMode: true, pageExtensions: ['js', 'jsx', 'md', 'mdx'], };
Notice that we use the ES6 import
and export
statement because we can only import remark-frontmatter
rather than using the require
statement. So with that, you can rename next.config.js
to next.config.mjs
.
The code above simply configures Next.js so we can use .mdx files as pages in Next.js. The options parameter is where we can input our plugins (remark and rehype). We only need a remark plugin, which is remark-frontmatter
.
Also, in the options
parameter, we have the providerImportSource
, which is for parsing our React context provider from @mdx-js/react
, which we installed earlier.
Just at the end of the webpack
method, we passed in some other Next.js configurations. But, let’s add one more configuration for our Next.js Image
loader:
export default { //.. pageExtensions: ['js', 'jsx', 'md', 'mdx'], images: { loader: 'imgix', path: 'https://images.unsplash.com/', }, //... };
Create your first Next.js page in MDX
Under the pages
directory, create a new file called about.mdx
. Now, you can write any markdown or/and JSX content, or simply copy-paste the following:
import Head from 'next/head'; <Head> <title>Hello World - PressBlog</title> </Head> # Hello World
Start your Next.js server with npm run dev
and visit your new /about
page in the browser. You should receive the “Hello World” message with a “Hello World – PressBlog” as the title.
This usually would throw a 404 error, but thanks to the configuration we added in next.config.mjs
, we can create pages with MDX.
Now that we have everything set up, let’s start organizing our blog, shall we?
Organize our app
How would you organize and structure your files? We just created a page in MDX, right? So, should we create all of our pages in MDX, and then create custom components (widgets, you might say) in JavaScript? But, if we can create custom components in MDX too, then what’s stopping us from just creating everything and anything in MDX?
Well, the truth is organizing your blog with MDX can be confusing at first. Because now you have two tools that can do almost the same thing, the question would be which should you use and when should you use it?
To answer this, we need to first understand that the purpose of MDX is not to replace JSX in all use cases. The purpose of MDX is so we can produce interactive and dynamically rich content in our Markdown easily.
So, although MDX can be used to make a header
component that only contains the name and logo of the blog with some links, for example, we shouldn’t use it because that would just be a waste of a tool.
When you have lots of content to write, you should use MDX rather than JSX. In a blog like this, we only have lots of content in our articles. That’s why in most MDX use cases, you can hardly find an .mdx
file in the components folder because they don’t need it there.
Now that we decided to only use MDX for our articles, we must now decide how our articles will be structured. We’ve seen earlier that it is possible to create a page in MDX. Wouldn’t it be simpler to have all of our posts/articles as a page in MDX?
What this means is we can have a posts
directory under the pages
directory in our Next.js app, and we can simply write all our posts in MDX in this directory.
For example, this would look like an article as pages/posts/what-is-react-native.mdx
and another article as pages/posts/where-to-learn-react-native.mdx
.
Traditionally, and alternatively, in Next.js, you would have a posts
directory (that contains your articles in Markdown) outside of the pages
directory, and another posts
directory inside the pages
directory with only one dynamic file (route).
That is, for example, pages/posts/[slug].js and fetching the required post in [slug].js based on the slug.
This is still possible, but it kind of kills one of the usefulness of MDX. In this article, we will use the first approach only, but I will also show you how you can fetch and parse MDX files from a directory, which is basically how the second approach works.
At the end of this tutorial, this is how the structure of our project will look (not including unchanged directories and files that come with Next.js by default):
PressBlog | |___ next.config.mjs | |___ components | |___ Header.js | |___ MDXComponents.js | |___ MeetMe.js | |___ Meta.js | |___ PostItem.js | |___ layouts | |___ Layout.js | |___ pages | |___ index.js | |___ about.mdx | |___ _app.js | |___ posts | |___ index.js | |___ learn-react-navigation.mdx | |___ what-is-react-native.mdx | |___ scripts | |___ fileSystem.js | |___ styles | |___ globals.css | |___ Header.module.css | |___ Home.module.css | |___ Markdown.module.css
Using CSS for styling
In this tutorial, we will only use the Next.js styling approach; no Tailwind, no Bootstrap. But, it is possible to use styled-components or any utility-first framework.
Let’s apply all used styles (which is minimal) for this blog here and now.
First, let’s add Header.module.css
, which applies to our header only. So copy and paste the following styles into it:
.header { padding: 10px 0; background-color: rgb(164, 233, 228); } .header > div { display: flex; justify-content: space-between; align-items: center; } .header li { list-style: none; display: inline-block; margin: 0 10px; }
Next, we’ll use globals.css
, which already exists, so you don’t have to create it, just add the following styles to it:
a { color: inherit; text-decoration: underline transparent; transition: 0.25s ease-in-out; } a:hover { text-decoration-color: currentColor; } .max-width-container { max-width: 680px; margin-left: auto; margin-right: auto; padding: 0 10px; } .max-width-container.main { padding: 50px 30px; } p { line-height: 1.6; }
Then, apply the Home.module.css
style, which will only apply to the home page:
.mainContainer { padding: 15px 30px; padding-top: 45px; } .articleList { margin-top: 55px; } .desc { color: #3b3b3b; font-style: italic; font-size: 0.85rem; } .articleList > div { margin-bottom: 50px; } .img { max-width: 100%; height: auto; border-radius: 50%; }
Finally, use Markdown.module.css
, which will apply only to our articles’ contents:
.postTitle { border-bottom: 2px solid rgb(164, 233, 228); padding-bottom: 13px; } .link { color: rgb(0, 110, 255); } .tooltipText { background: hsla(0, 0%, 0%, 0.75); color: white; border: none; border-radius: 4px; padding: 0.5em 1em; }
Creating the about page
For now, let’s work on our about page. Recall that we already created an about.mdx
page; now, we insert Lorem text into it. But before that, let’s want to create a component called MeetMe.js
. This component will contain an image and a short description of the author.
By creating it as a component, we can use it on the home page as well. So, go ahead and create a components
directory in the root directory of the app. Then, create a file called MeetMe.js
and copy and paste the following into it:
import Image from 'next/image'; import styles from '../styles/Home.module.css'; const MeetMe = () => { return ( <div> <Image src='photo-1618077360395-f3068be8e001?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NHx8bWFufGVufDB8MnwwfHw%3D&auto=format&fit=crop&w=500&q=60' alt='john doe avatar' width={150} height={150} className={styles.img} /> <p className={styles.p}> Hey, I am <strong>John Doe</strong>. I love coding. Lorem ipsum dolor sit, amet consectetur adipisicing elit. Reiciendis commodi numquam incidunt blanditiis quibusdam atque natus inventore sunt autem iusto. </p> </div> ); }; export default MeetMe;
Not a lot is happening here; we are simply importing the Image
component from next/image and also applying styles to the paragraph and image using the Home.module.css
styles, which we already created.
Now that we have the MeetMe
component set up, let’s import it into about.mdx
:
import MeetMe from '../components/MeetMe.js'; <MeetMe /> #### Let's dive into more of me Lorem, ipsum dolor sit amet consectetur adipisicing elit. Ea deserunt ab maiores eligendi nemo, ipsa, pariatur blanditiis, ullam exercitationem beatae incidunt deleniti ut sit est accusantium dolorum temporibus ipsam quae. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Ea deserunt ab maiores eligendi nemo, ipsa, pariatur blanditiis, ullam exercitationem beatae incidunt deleniti ut sit est accusantium dolorum temporibus ipsam quae. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Ea deserunt ab maiores eligendi nemo, ipsa, pariatur blanditiis, ullam exercitationem beatae incidunt deleniti ut sit est accusantium dolorum temporibus ipsam quae.
Lastly, before we check out of this page, let’s create a Meta
component that will contain our meta tags and title. So, head up to the components
directory and create a new file called Meta.js
, and copy and paste the following:
import Head from 'next/head'; const Meta = ({ title }) => { return ( <Head> <title>{title}</title> <meta name='keywords' content='react native, blog, John Doe, tutorial, react navigation' /> </Head> ); }; export default Meta; // let's set a default title Meta.defaultProps = { title: 'PressBlog - Your one stop blog for everything React Native', };
To keep things simple, we will only use static meta keywords and a dynamic title. In a real blog, you would need to optimize more for search engines.
Let’s import our new component Meta.js
into about.mdx
, and use as such
{ /* .. */ } import Meta from '../components/Meta.js'; <Meta title='About John Doe - PressBlog' />; { /* .. */ }
With that, we can save our files and test our about page.
Creating the layout
The next thing we’ll work on is the layout of the blog. Before we proceed, we need a header component, so let’s create it. Head up to the components
directory and create a new file called Header.js
and copy and paste the following:
import Link from 'next/link'; import styles from '../styles/Header.module.css'; const Header = () => { return ( <header className={styles.header}> <div className='max-width-container'> <h2> <Link href='/'>PressBlog</Link> </h2> <ul> <li> <Link href='/posts'>Blog</Link> </li> <li> <Link href='/about'>About</Link> </li> </ul> </div> </header> ); }; export default Header;
Here, we use the Link
component from next/link
. Also, we use the Header.module.css
styles, which we created earlier.
There is a tiny bit of change for the styles, however. Notice that we used the class name max-width-container
, which is not from Header.module.css
. It is not a typo, though; the class name was previously used in globals.css
because we need it globally.
Next, let’s create a directory called layout
in the root directory and create a file in it called Layout.js
:
import Header from '../components/Header'; const Layout = ({ children }) => { return ( <div> <Header /> <main className='max-width-container main'>{children}</main> </div> ); }; export default Layout;
Here, we import the Header
component we just created and pass it as the header of our blog layout. You can do the same with a footer, but we are not using a footer for this blog.
Now, to apply the Layout
component to our app, head up to pages/_app.js
and wrap the Layout
component around the Component
component:
//.. return ( <Layout> <Component {...pageProps} /> </Layout> ); //..
Adding the articles
In this section, we will create two articles. We will use these articles later on the home page and the blog page (all blog posts). But for now, we will just create them and test them as single pages.
Let’s create a new directory called posts under the pages
directory. In this new directory, create the following MDX files.
We’ll start with learn-react-navigation.mdx
:
--- title: React Navigation Tutorial publishedOn: January, 6th. 2022 excerpt: React Navigation is a number one tool to learn in React Native. You would always use them; these are some of the best practices in using React Navigation --- import Meta from '../../components/Meta'; import { Disclosure, DisclosureButton, DisclosurePanel } from "@reach/disclosure"; <Meta title='React Navigation Tutorial - PressBlog' /> # React Navigation Tutorial React Navigation is one of the best things that happened to [**React Native**](https://reactnative.org) ## Benefits of using React Navigation 1. It is cool to use 2. It is simple to learn 3. It has an extensive community 4. And more and more <Disclosure> <DisclosureButton as='div'>React Navigation won't be here forever. Click to find why</DisclosureButton> <DisclosurePanel>Nothing lasts forever, yeah! That's all I got to say</DisclosurePanel> </Disclosure>
The Disclosure
components and their counterparts only demonstrate using components and widgets in MDX, and how useful it can be for your articles. Our next article uses a tooltip, so let’s check it out by using what-is-react-native.mdx
:
--- title: What is React Native? publishedOn: January, 7th. 2022 excerpt: Lorem, ipsum dolor sit amet consectetur adipisicing elit. Ea deserunt ab maiores eligendi nemo, ipsa, pariatur --- import Meta from '../../components/Meta.js'; import Tooltip from '@reach/tooltip'; import '@reach/tooltip/styles.css'; <Meta title='What is React Native - PressBlog' /> # What is React Native? React Native is a lovely language <Tooltip label="It's not actually a language though"> <button>Fact about React native</button> </Tooltip>
You can test each of your articles in the browser to see if it works. Notice that the part that contains our metadata doesn’t show up in the browser, which is a good thing because we don’t want our readers to see this.
This is possible with remark-frontmatter
, which we installed and configured in next.config.mjs
.
Adding article-specific styles
Let’s add styles that are specific to our blog articles only. With the approach we will use, we will be able to style any element in MDX.
Note that when I say element, I mean when it compiles into JSX, for example, # Hello world
in MDX compiles to h1
in JSX. So, with this approach, we can style all h1
elements in our MDX.
The way this is done is by wrapping our app components with the MDXProvider
from @mdx-js/react
, and providing an object of components for mapping as many elements as needed.
So, head up to the components directory and create a new file called MDXComponents.js
, and copy and paste the following code into it:
import styles from '../styles/Markdown.module.css'; const MDXComponents = { p: (props) => <p {...props} className={styles.p} />, a: (props) => <a {...props} className={styles.link} />, h1: (props) => <h1 {...props} className={styles.postTitle} />, }; export default MDXComponents;
Here, we import the styles we created earlier for Markdown contents only. Each key of the MDXComponents
object is a component that corresponds to whatever MDX compiles to.
You can now decide to create custom components for any JSX elements:
import { Sparky } from 'sparky-text'; // a dummy module - does not exist import Link from 'next/link'; import styles from '../styles/Markdown.module.css'; const MDXComponents = { strong: ({ children }) => <Sparky color='gold'>{children}</Sparky>, p: (props) => <p {...props} className={styles.p} />, a: (props) => <Link {...props} className={styles.link} />, h1: (props) => <h2 {...props} className={styles.postTitle} />, // for some reasons I want h1 to be mapped, styled and displayed as h2 }; // please do not use this example, this is only a demonstration
Right now, our MDXComponents.js
has no effect. So, to fix that, head up to pages/_app.js
and import MDXProvider
from @mdx-js/react
, and the MDXComponents
component we just created.
Wrap the MDXProvider
around the Layout
component and pass MDXComponents
as a component prop for MDXProvider
. In summary, your _app.js
should look like this:
import '../styles/globals.css'; import { MDXProvider } from '@mdx-js/react'; import MDXComponents from '../components/MDXComponents'; import Layout from '../layouts/Layout'; function MyApp({ Component, pageProps }) { return ( <MDXProvider components={MDXComponents}> <Layout> <Component {...pageProps} /> </Layout> </MDXProvider> ); } export default MyApp;
Creating the home page
The home page is made up of an introductory section and a list of ten articles.
Let’s begin by fetching these ten articles (we don’t have up to ten, but it will work fine) using the Node.js file system and path module. To do this, let’s create a directory called scripts
in the root directory, and within that, create a file called fileSystem.js
.
The function we’ll create in fileSystem.js
fetches all posts (that is, from the pages/posts
directory) in MDX. This function can then be used in Next.js’ getStaticProps
function.
So, this is how we can fetch all posts in fileSystem.js
:
- Get
.mdx
files inpages/posts
using the Node.js system module’sreaddirSync
andreadFileSync
methods - Parse each files’ Front Matter into an object of metadata using
gray-matter
- Generate a slug for each of the files using the filename
Now, let’s see the code. In fileSystem.js
, copy and paste the code below:
import fs from 'fs'; import path from 'path'; import matter from 'gray-matter'; const getPosts = (limit) => { const dirFiles = fs.readdirSync(path.join(process.cwd(), 'pages', 'posts'), { withFileTypes: true, }); const posts = dirFiles .map((file) => { if (!file.name.endsWith('.mdx')) return; const fileContent = fs.readFileSync( path.join(process.cwd(), 'pages', 'posts', file.name), 'utf-8' ); const { data, content } = matter(fileContent); const slug = file.name.replace(/.mdx$/, ''); return { data, content, slug }; }) .filter((post) => post); if (limit) { return posts.filter((post, index) => { return index + 1 <= limit; }); } return posts; }; export default getPosts;
This is quite a lot, but let’s go through each line.
The first thing we do is import our modules, which are fs
(from Node.js), path
(from Node.js), and matter
(from gray-matter
).
Next, we create a getPost
function with just one parameter limit and export it. The readdirSync
then gets all files in the pages/posts
directory with their file extension and is assigned to the dirFiles
variable.
Now, we loop through the dirFiles
to first filter out all files that aren’t MDX. While in the loop, we can read each file’s content using the readFileSync
method, and we pass the content as a parameter for the matter
function.
The matter
function then returns an object in which we only need data
and content
. data
is the metadata that we used Front Matter to write in each of our articles, and content
is the remaining content of the articles.
Finally, in the loop, we generate a slug from the filename (without the extension) and return an object of data
, content
, and slug
from our loop function.
Apart from the home page, the blog page will also use the getPost
function, but the blog page will not have any limit, which is the reason for the condition.
In getting all your posts, you want to make sure you’re not doing it asynchronously, which is the reason for the readdirSync
and readFileSync
because they are both synchronous.
Now we can open up pages/index.js
and clear out all tags and import statements (it comes by default for every Next.js app). Create a new function, getStaticProps
, outside the index
function, and copy and paste the code below into it:
export const getStaticProps = () => { const posts = getPosts(10); return { props: { posts, }, }; };
If you’ve worked with Next.js, this is very straightforward. But if you haven’t, it is a function used for data fetching in Next.js. Next.js uses the props returned from the exported getStaticProps
function to prerender our index.js
page at build time. This process is called static site generation.
You can perform lots of filtering and sorting here. For instance, if you want to get the last ten published articles, not just any ten, you can sort it out with the publishedOn
metadata returned from each post.
However, note that you must export the getStaticProps
function, else it would just be like any other function. In this function you can fetch data from a CMS or your API.
Next.js gives us the comfort of using the props returned from the getStaticProps
in our index
function. What this means is our index
function on this page (pages/index.js
) will have posts as props passed into it:
const index = ({ posts }) => { return ( <> <Meta /> <MeetMe /> <Link href='/about'>More about me</Link> <div className={styles.articleList}> <p className={styles.desc}>Newly Published</p> {posts.map((post) => ( <p key={post.slug}>{post.data.title}</p> ))} </div> </> ); }; export default index;
By using the Meta
component to add meta tags to our page, the MeetMe
component displays a short description of the author, in which there is a link to a full description on the about page.
Lastly, we loop through the posts
array coming as a prop to our index
function. You should also import the components and functions used in index
and getStaticProps
.
Here is a code snippet that shows all this in pages/index.js
:
import MeetMe from '../components/MeetMe.js'; import Link from 'next/link'; import getPosts from '../scripts/fileSystem'; import styles from '../styles/Home.module.css'; import Meta from '../components/Meta'; const index = ({ posts }) => { return ( <> <Meta title='PressBlog - Your one stop blog for anything React Native' /> <MeetMe /> <Link href='/about'>More about me</Link> <div className={styles.articleList}> <p className={styles.desc}>Newly Published</p> {posts.map((post) => ( <p key={post.slug}>{post.data.title}</p> ))} </div> </> ); }; export default index; export const getStaticProps = () => { const posts = getPosts(10); return { props: { posts, }, }; };
As for now, we are only displaying each article’s title in a paragraph, which is far from what we want. Let’s go ahead and create a new component called PostItem
.
To do this, create a file in the components
directory called PostItem.js
, and copy and paste in the code below:
import Link from 'next/link'; const PostItem = ({ post }) => { return ( <div> <h3> <Link href={`/posts/${post.slug}`}>{post.data.title}</Link> </h3> <p>{post.data.excerpt}</p> <Link href={`/posts/${post.slug}`}>Read more</Link> </div> ); }; export default PostItem;
Now, head back to pages/index.js
and import the PostItem
component we just created. Then, replace the paragraph that displays the title of each post with <PostItem key={post.slug} post={post} />
:
//.. <div className={styles.articleList}> <p className={styles.desc}>Newly Published</p> {posts.map((post) => ( <PostItem key={post.slug} post={post} /> ))} </div> //..
Finally, save the files and test the home page in your browser. This is the same principle we will apply to the blog page.
Creating the blog page
This is the last page, I promise. This page displays all of our posts on one page. So, to get started, create a new file called index.js
in the pages/posts
directory and paste in the following:
import getPosts from '../../scripts/fileSystem'; import PostItem from '../../components/PostItem'; import styles from '../../styles/Home.module.css'; import Meta from '../../components/Meta'; const index = ({ posts }) => { return ( <div> <Meta title='Blog posts - PressBlog' /> <p className={styles.desc}>All Posts</p> {posts.map((post) => ( <PostItem key={post.slug} post={post} /> ))} </div> ); }; export default index; export const getStaticProps = () => { const posts = getPosts(false); return { props: { posts, }, }; };
This is just like what we saw on the home page, only we made a few changes to the index
function. Though, take note of the argument passed into the getPosts
function in getStaticProps
. With the false
value, all of our posts (that is, all MDX files in pages/posts
) will be returned.
Now, save the file and test the new blog page (visit /posts
in your browser). With that, we’ve successfully built a blog with Next.js and MDX.
Conclusion
Creating an interactive blog or documentation has never been this easy with Markdown. If you followed along, you are one step away from having a blog of your own. You can decide whether to add more features before deploying.
If you hope to build documentation, this tutorial can help you as well, or you can simply use Nextra. Nextra uses Next.js and MDX to create a website for your documentation, and of course, you get to write all your content in MDX.
To round it all up, in this article we learned about an interesting tool called MDX, what we can do with it, like writing expressions and passing props, and creating a blog with MDX and Next.js.
In building this blog, we saw how we can configure MDX to work in Next.js, how we should structure our apps, and how we can fetch and parse MDX files into an object of metadata (from Front Matter) and content.
So, that’s it. Thanks for reading.
The post Create a Next.js and MDX blog appeared first on LogRocket Blog.
from LogRocket Blog https://ift.tt/NUGnL1P
via Read more