If you're looking to create a blog site with modern web technologies, Next.js is an excellent choice. Next.js is a powerful React framework that offers server-side rendering, static site generation, and a host of other features that make it perfect for building fast and SEO-friendly websites. In this blog post, we'll walk through the steps to create a blog site using Next.js.
Before we dive into the technical details, let's briefly discuss why Next.js is a great option for a blog site:
Before getting started, ensure you have Node.js and npm (Node Package Manager) installed. You can download and install them from Node.js official website.
Use the following command to create a new Next.js app:
npx create-next-app my-blog
cd my-blog
This will set up a new Next.js project in the my-blog
directory.
Navigate to your project directory and start the development server:
npm run dev
Open your browser and go to http://localhost:3000
to see your new Next.js app in action.
Next.js uses a file-based routing system. To create a new page, simply add a new file to the pages
directory. Let's create a homepage and a blog post page.
Create pages/index.js
for the homepage:
import Link from 'next/link';
const Home = () => {
return (
<div>
<h1>Welcome to My Blog</h1>
<p>Check out my latest posts:</p>
<ul>
<li><Link href="/posts/first-post">First Post</Link></li>
{/* Add more links to posts here */}
</ul>
</div>
);
};
export default Home;
Create pages/posts/first-post.js
for the first blog post:
const FirstPost = () => {
return (
<div>
<h1>First Post</h1>
<p>This is the content of the first post.</p>
</div>
);
};
export default FirstPost;
To avoid repeating code, it's a good practice to create a layout component that can be used across multiple pages. Create a components
directory and add a layout.js
file:
const Layout = ({ children }) => {
return (
<div>
<header>
<h1>My Blog</h1>
<nav>
<ul>
<li><Link href="/">Home</Link></li>
{/* Add more navigation links here */}
</ul>
</nav>
</header>
<main>{children}</main>
<footer>© 2024 My Blog</footer>
</div>
);
};
export default Layout;
Now, use this layout in your pages:
// pages/index.js
import Layout from '../components/layout';
const Home = () => {
return (
<Layout>
<h1>Welcome to My Blog</h1>
<p>Check out my latest posts:</p>
<ul>
<li><Link href="/posts/first-post">First Post</Link></li>
{/* Add more links to posts here */}
</ul>
</Layout>
);
};
export default Home;
// pages/posts/first-post.js
import Layout from '../../components/layout';
const FirstPost = () => {
return (
<Layout>
<h1>First Post</h1>
<p>This is the content of the first post.</p>
</Layout>
);
};
export default FirstPost;
Next.js supports CSS and Sass out of the box. You can create a styles
directory and add your stylesheets there.
Create styles/global.css
and import it in your _app.js
:
/* styles/global.css */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
box-sizing: border-box;
}
header, footer {
background-color: #333;
color: #fff;
padding: 1rem;
}
nav ul {
list-style: none;
padding: 0;
}
nav ul li {
display: inline;
margin-right: 1rem;
}
main {
padding: 1rem;
}
Import the stylesheet in pages/_app.js
:
import '../styles/global.css';
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
export default MyApp;
For a real blog, you need to fetch data from a CMS or a local file. For simplicity, we'll use local markdown files in this example. Install gray-matter
and remark
for parsing markdown:
npm install gray-matter remark remark-html
Create a posts
directory at the root of your project and add a markdown file first-post.md
:
---
title: "First Post"
date: "2024-06-27"
---
This is the content of the first post.
Create a utility function to fetch and parse markdown files:
// lib/posts.js
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import { remark } from 'remark';
import html from 'remark-html';
const postsDirectory = path.join(process.cwd(), 'posts');
export function getSortedPostsData() {
const fileNames = fs.readdirSync(postsDirectory);
const allPostsData = fileNames.map(fileName => {
const id = fileName.replace(/\.md$/, '');
const fullPath = path.join(postsDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, 'utf8');
const matterResult = matter(fileContents);
return {
id,
...matterResult.data
};
});
return allPostsData.sort((a, b) => {
if (a.date < b.date) {
return 1;
} else {
return -1;
}
});
}
export async function getPostData(id) {
const fullPath = path.join(postsDirectory, `${id}.md`);
const fileContents = fs.readFileSync(fullPath, 'utf8');
const matterResult = matter(fileContents);
const processedContent = await remark()
.use(html)
.process(matterResult.content);
const contentHtml = processedContent.toString();
return {
id,
contentHtml,
...matterResult.data
};
}
Update your pages to use the fetched data:
// pages/index.js
import Layout from '../components/layout';
import { getSortedPostsData } from '../lib/posts';
import Link from 'next/link';
export async function getStaticProps() {
const allPostsData = getSortedPostsData();
return {
props: {
allPostsData
}
};
}
const Home = ({ allPostsData }) => {
return (
<Layout>
<h1>Welcome to My Blog</h1>
<ul>
{allPostsData.map(({ id, title }) => (
<li key={id}>
<Link href={`/posts/${id}`}>{title}</Link>
</li>
))}
</ul>
</Layout>
);
};
export default Home;
// pages/posts/[id].js
import Layout from '../../components/layout';
import { getAllPostIds, getPostData } from '../../lib/posts';
export async function getStaticPaths() {
const paths = getAllPostIds();
return {
paths,
fallback: false
};
}
export async function getStaticProps({ params }) {
const postData = await getPostData(params.id);
return {
props: {
postData
}
};
}
const Post = ({ postData }) => {
return (
<Layout>
<h1>{postData.title}</h1>
<div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
</Layout>
);
};
export default Post;
Update your lib/posts.js
to include the getAllPostIds
function:
// lib/posts.js
export function getAllPostIds() {
const fileNames = fs.readdirSync(postsDirectory);
return fileNames.map(fileName => {
return {
params: {
id: fileName.replace(/\.md$/, '')
}
};
});
}
You've now built a basic blog site using Next.js! You have a homepage that lists all blog posts, individual blog post pages, and dynamic routing to handle different blog post URLs
. You can expand this setup by adding more features like a CMS integration, comments, categories, tags, and more.
Next.js makes it easy to create fast, SEO-friendly websites with a great developer experience. Happy coding, and enjoy building your blog!