How to Code a Blog in 2025
(with MDX and Next14 Pages-Routing)
February 09, 2023 - Updated January 2024 - 28min - #blog #nexjs #tutorial #webdev #docs
Welcome to the step-by-step guide of how you can build a blog-website just like this - using React, Tailwind, NextJS, MDX and MailChimp.
Why use pages routing in 2025/ Next14? Eventhough NextJS is nudging everyone to use App-Routing, at work you will likely encounter older projects that still use pages routing. It's a valuable skill to know the legacy ways.
Why MDX with pages routing? Besides interactive React components in your articles: In pages routing you can route with your blog-post-title.mdx files, and render them as pages directly without needing to parse them through the usual [slug/id].js file. This way you also don't need getStaticRoutes.
To build this setup, I had to pick pieces from many different tutorials. In this guide I bring it all together.
Table of Contents
Disclaimer: The instructions of this guide are just my personal recommendations. Feel free to customize to your own taste.
0. Prerequisites
If you are a complete beginner at coding, first work through something like the Webdeveloper Bootcamp by Colt Steele. That's what gave me the foundations for this blog project (HTML, CSS, JavaScript, React).
For learning Tailwind, scan through this tutorial on 1.5-2x speed. Then learn to actually use it by building your own blog with it. With Tailwind-Docs-Search and ChatGPT, this is fun and easy. Except for some basic CSS overview, I had almost zero experience with it before this blog.
For the harder part of styling - positioning and responsive design - I also wrote intructions later in this article. Additionally you can find all tailwind-classes that I used in my source code on Github.
If you are new to to Github/ version control: Work through this Git Crash Course. And make sure to read through conventional commits.
Accessibility is not my strength yet, so I only adress a few of its aspects in this guide. If you want to go deeper on it, my mentor recommends this resource.
IMPORTANT: Before starting your blog project, I can also recommend to watch this little NextJS course on 1.5-2x speed (no need to code along there imo). This will give you a workable overview of the NextJS basics.
1. Preparation and Setup
Working along this structure made the process smooth and straight-forward:
- preparation
- modeling (reference ui/ux)
- drafting (visual and written)
- mockup content
- component plan
- setup
Vision/ Scope
Modeling
If you want to build websites as cool as your idols - steal from them. Seriously, everything is a remix. Pick whatever sparks your fire, even outside the box of websites.
I stole from:
- bettermotherfuckingwebsite.com (main content width and breath)
- nav.al (subscribeform in header)
- overreacted.io (header content centered and colorfulness)
- AbletonLive Music Production Suite (tag colors I used in my producer days)
Drafting
Visual
Draft out the design first. At least roughly. Positioning, proportions, colortheme, font vibe, most important elements and pages. I used Photoshop, but Gimp or even pen and paper could do. IMPORTANT: Also draft out how it should look on mobile aswell!
Knowing precisely what I wanted to build, and seeing it almost alive from the start, felt really helpful to me. This kept me motivated and always clear about the next steps.
It is also a good measurement of how closely you can create what you (or your designers) have planned. Of course you can keep happy accidents, but don't take them as an excuse to not create and learn what you wanted to in the first place.
I am pretty proud of how close I got:
(my first Photoshop draft of the homepage)
Written
Brainstorm notes of all the elements, features and effects you want as bulletpoints.
Include stuff that you are not sure about. Just tag that with question marks. Sort and trim later.
Use these notes and this article as the source for your to do lists.
Mock Up content
Create placeholder articles and other page content using the LLM of your choice.
It's easy and makes the building process more fun and realistic.
Here are the prompts I used:
- generate 5 interesting article titles for my dev blog
- format as previews, including a random date and a preview sentence
- write out the first one as a full article including code examples
- write me a short about page
Component Plan
Brainstorm a list of all the major (react-) components and pages you will likely need to build, like this:
- HomeFeed (index/ home)
- Header
- Footer
- Menu
- Layout (main template)
- SubcribeForm (header, footer, menu, onpage)
- About
- 404
Setup
Installation
-
install and run the NextJS project as described in the NextJS docs:
- install Node.js and npm
npx create-next-app@latest
- installation settings:
- What is your project named? blog
- Would you like to use TypeScript? No / Yes
- Would you like to use ESLint? No / Yes
- Would you like to use Tailwind CSS? No / Yes
- Would you like to use
src/
directory? No / Yes - Would you like to use App Router? (recommended) No / Yes
- Would you like to customize the default import alias (@/*)? No / Yes
cd blog
npm run dev
- open http://localhost:3000/ in the browser (chrome/ brave)
-
delete all unneccessary components of the NextJS default template
git branch empty-template
to save this as a canvas for css experiments later in the process
Routing
NextJS pages-routing in a nutshell:
NextJS PR determines the URL path of your different pages by their position in the folders and a set of special purpose filenames. Make sure to use this exact folder structure and the rules described in the comments.
Action Steps:
- create all of the component and page files from the component list
- place them in their routing as described above
- no need for feedData.js and getStaticProps yet
- components like Footer.js belong into the component folder
- the files can be empty but should have the right filenames and folderpositions
- you can already paste your mockup article texts into your .mdx files though
- test if the routes exist, by typing them in the URL bar of the browser
- congrats, you now understand the pages-routing basics, let's learn how to build on top of that in the main building section
2. Main Building
In this section I explain most of the smaller steps of the main building process.
To keep it organized, I wrote the bigger challenges into seperate, detailed articles. You can find them here:
- MDX & Tailwind in Next 14 PAGES-routing (incl. Syntax Highlighting)
- Mailchimp Newsletter Management & Custom SubscribeForm | Next API
- How to build a "sticky footer" for your WebApp | CSS Height & Positioning
Components and aspects covered in this section:
HTML Overview
Before we go into the components, there are a few HTML concepts that are helpful to keep in mind for the project:
- overview
You can see that there are 3 layers of nesting in a NextJS PR project:
- the "HTML base layer" (_document.js)
- the "layout and settings layer" (_app.js)
- and finally the individual pages
Having this overview helps to structure your (Tailwind-)CSS stylings in the correct order of nesting, and detect conflicting classes. This is crucial for the usually cumbersome CSS positioning, which I go into in this dedicated article.
<main>
Notice the <Main/>
tag in the base and the <main></main>
in the page layer.
The first is a NextJS specific Main that does not actually exists in the dom
tree and the second represents the mains of the individual pages.
No tutorial stressed out the role of the <main></main>
tag to me - or let alone
that every page needs one. It turns out, besides CSS clarity, it is
essential for accessibility and search engine optimization (SEO).
I only knew that you should often use descriptive tags like <section>
instead of <div>
, but I
wasn't aware of the best practice <main>
-structure, until my mentor pointed it
out to me... and now you know it too:
main tag rules
- every page needs a
<main>
as its highest parent <article>
tags are important (eg. for RSS), but they don't replace the role of the<main>
and should be nested inside of it- only have one
<main>
per page/ DOM-tree- make sure to check for "doubles" through all layers via the devtools elements section
- don't worry, the NextJS
_document.js
<Main/>
tag does not appear in the client-side DOM-tree, so the "only one<main>
per DOM-tree"-rule can be fulfilled
- don't worry, the NextJS
- as there is no
<layout>
html-tag, I falsly used<main>
tags for that, as it seemed more descriptive than<div>
- instead, to make layout layers more descriptive you can use accessibility attributes
like
<div role="region" aria-label="Main/MDX Layout">
- instead, to make layout layers more descriptive you can use accessibility attributes
like
- make sure to check for "doubles" through all layers via the devtools elements section
- menues and navbars are lists
Here is another accessibility (and code organization) structure I was ignorant of:
Navbars usually contain <a>
tags to different sub pages of your website.
Instead of just stacking them as siblings one after another inside a <div>
,
place them in <li>
tags in a <ul>
. This way it is clear to the screen reader
that
- they are basically a navigation menu sitting at the same layer,
- belonging together,
- and having a clear start and endpoint,
- even when they have more complex menu nestings.
Ok, you are ready for the first components, let's go!
Header & Footer
The header component on its own is simple. Just follow the structure and comments.
The Footer component is almost the same.
Layout
Now that you have Header
and Footer
, you can build the NextJS Layout
component.
It is also pretty simple and self explanatory:
What might get a little tricky to understand is the{ children }
prop. It is
built into React and represents all the content that you wrap inside of a
components tag. Component-wrapped children props get auto-passed upwards and are
accessible as soon as you type children
into the parents props-destructuring ({ })
.
As you can see below, inside of a NextJS project, the Layout
component is
wrapped around some other essential layers within the MyApp
component.
Congrats, now you also understand _app.js
.
HomeFeed
Just like in this NextJS docs tutorial, you want the index page to act as
a feed with previews of your articles. For this you need a HomeFeed
component and a
metadata utility component in a separate file.
First, make sure you write metadata in frontmatter format at the very top of your articles, like this:
Next, parse this metadata into a getFeedData
component in your utility file.
This component returns an array [{},{},{},...]
from all the posts, sorted by
date, with one object per post. Each object contains key-value pairs of the slug
and the frontmatter metadata out of the .mdx files.
Then finally you import getFeedData
into your Homepage, fetch the feedData via getStaticProps
and populate the feed.
Create this file for the date formatting needed above:
Fetching via getStaticProps helps NextJS to pre-render the data so it can send only the final dom-tree to the browser. You have to do this in the same component file, where you want it to be rendered. Otherwise you would have to import the data from another file and send it to the browser together with the component to be computed clientside.
Remember: We are using .mdx files directly as pages in this setup, so we don't
need the usual [id].js
file with getStaticRoutes
for parsing vanilla markdown files as
pages. That's why the getFeedData.js
file is also a lot simpler than in most NextJS
tutorials.
Menu Component
Instead of using a premade menu component from a library like shadzcn, you can build your my own custom one.
The Menu
component itself is pretty simple, as most of the logic is sitting in
the Header
component where the Menu
is nested and lifting its state to. The main
"trick" here, is the conditional rendering and passing the right props to the
right places:
Now take a look at the Header
component again. In the "Header and Footer" section earlier I left out the Menu
logic to keep it focused.
Here you can see all of it included and explained:
You could also build a Menu
component that has main logo h1
at the same spot
as the Header
and just overlays it. This way the Header
(parent) does not
need to know about the Menu
(child) state at all, so you could move all of the
logic down into the child.
In bigger projects this might makes sense because every state change creates a re-render of both the parent and the child. In this case it is ok though, because the state only changes when interaction with the menu is happening and both components are lightweight anyway.
*I called the prop-attribute "recieved" to emphasize that the attribute name can be chosen freely. Props are passed through the value assigned to the attribute, not through the attribute name itself. Metaphor: The attribute name is like a child holding out a named bucket to the parent saying "You can put the prop in here."
Responsive Design
In Tailwind, the breaking points are css mediaqueries pointing in one direction:
from smaller to bigger screen sizes. This means unprefixed classes like flex
apply to all sizes, but prefixed classes sm:flex
apply to the specified
breakpoint and above.
Because of this, it makes sense to start the positioning part of the styling from the smallest screens (simulate via chrome devtools F12) and work your way upward from there.
This way you set the similarities for all sizes first and then only add the differences via breakpoints.
I made the mistake to only draft the desktop screen design in Photoshop beforehand, so I also started with desktop styling. After I was done with that, I realized I had to redo much of the work and start over from mobile screen size.
So you better draft out the mobile design beforehand aswell, so you can start from there. Tipp: Check how the websites you chose to model behave in mobile screens, to get a feel for the styling.
Wax on Wax off: Each time you make styling changes, check for any wierd misalignments in all of the dev tools screen sizes over and over again. I often stumbled upon meaningful styling problems, by testing the edgecases this way. The sooner you find them the better.
Positioning is one of the major parts of responsive design, so if you are having problems with that, also make sure to check out my article on positioning in general.
Custom Breaking Points: Don't be afraid to create custom breaking points in Tailwind. At first I tried to force myself to only use the default breaking points, but sometimes there are misalignments, that can only be fixed this way. Make use of the tools at your fingertips.
Font
If you always have internet in your dev environment, you can use the non-local
import
(commented out). If you sometimes code where there is no wifi, I
recommend using localFont
:
Sometimes, if your browser temporarily uses the fallback font, it can significantly change the positioning of some elements and the overall vibe of the design. LocalFont keeps your design stable at all times. Make sure to pick your font and install it before starting with the CSS styling part of your project.
To use localFont
, download the fontfile (preferrably a variable font) and save
it into /src/styles/fonts
. Then just copy the structure and import from below and use
${myFont.className}
to insert the font into the CSS of the Main Layout <div>
.
_document.js
and _app.js
have some NextJS specific rules, that prohibit
importing next/font
, so the highest parent file possible to set the font in is
Layout.js
.
Size matters! For most of the process, I had all text one or two (Tailwind-) sizes bigger. After a while I noticed a feeling as if I was working on a device for elders or toddlers. You know these phones with giant buttons?
I compared fontsizes with my reference websites, and I realized, that I had taken readability one step too far. So I sized down. It made a GIANT difference: Finally, I feel sleek as Apple.
404 Page Design
To mix in some fun, I designed a fancy 404 page. I've always wanted to use ASCII art and go ham with synth wave colors and gradients. This could be your chance for experiments too!
The ASCII art needed some extra formatting to get rendered properly:
- disable prettier
- linebreaks after each line
- a character at the start of each line (to keep the whitespaces before the first ascii characters)
- To make it look good with these extra charachters, I added characters all around the ascii content, which now serves as a border/ frame.
Tipp: Check out the 404 pages of your reference websites, to get a feel for a good 404.
404 redirect: Some tutorials build an automatic redirect from the 404 back to home. I tried that aswell, but it comes with problems:
- If you click to another page in the meantime, you will get redirected to home shortly afterwards.
- You might confuse the user, about what happened, if he did not notice the 404 sign in between the redirect.
- The user will not have the chance to sufficiently marvel at your beautiful 404 page.
Just do it like google: Present a link that the user can manually click for redirection.
Favicon
A small but important detail is to set your favicon - the icon of your blog that gets shown in the browser tab and in search engine results.
If you don't set it, you let the browser show a default icon. This looks like your website is broken, offline, or out of date. You don't need much to look professional.
Store the favicon in /public
and insert is like this:
Congratulations, you now know how to build all the foundational components. Make sure to check out the articles about the rest of this project:
- MDX & Tailwind in Next 14 PAGES-routing (incl. Syntax Highlighting)
- Mailchimp Newsletter Management & Custom SubscribeForm | Next API
- How to build a "sticky footer" for your WebApp | CSS Height & Positioning
3. References
Here is a list of all the references mentioned throughout the article:
- Webdeveloper Bootcamp by Colt Steele
- TailwindCSS Crash Course by Code with Guillaume
- TailwindCSS Docs
- LeonWarscheck.com Source Code on Github
- Github Crashcourse by Academind
- Conventional Commits
- Accessibility in React - MDN Article
- Gimp for drafting your Blog design
- NextJS Pages Routing Crash Course by NetNinja
- NextJS Docs: Installation (make sure to select Pages Routing Mode)
- NextJS Docs: Pages Routing Tutorial
- NextJS Docs: Font (make sure to select Pages Routing Mode)
And once again my additional articles about this blog project:
- MDX & Tailwind in Next 14 PAGES-routing (incl. Syntax Highlighting)
- Mailchimp Newsletter Management & Custom SubscribeForm | Next API
- How to build a "sticky footer" for your WebApp | CSS Height & Positioning
Finally, here is an important message to you - by Yoda:
If this far you read, subscribe you shall.