DEV Community

shrey vijayvargiya
shrey vijayvargiya

Posted on

Simplest and quickest blog website

Build a static content website using Nextjs React

Originally published on iHateReading

Hello and welcome to the new blog

In today's story, we will go through the way to build the simplest static blog website

Nextjs and React are JavaScript-based frameworks mainly dependent on JavaScript, and that brings some slowness to them on the browser or website built using the same are a bit slow.

But a static content website with Nextjs is fast and gives some powerful tools like React, Server Side API, if needed, dynamic APIs and a lot more, and that is why for a static content website I'll still prefer Nextjs and React

Last month, I launched this new website, gettemplate.website, and for this, I need to quickly launch a blog post.

One good way is to build an admin with an editor to write blogs and a dashboard to view and manage blogs, but it takes of lot of CRUD operations and style management, which need time, and our deadline was about a few hours to quickly put a few blogs live on the website.

Creativity comes when limitations arise, so I was thinking of some quick, cheap way to just write markdown content in a file and simply render the file content using Nextjs server-side API, or a simple fs module can also be used to read the file content.

Here is our way,

  1. First, store or write all blogs in markdown format in the content directory or folder
  2. Inside the content directory, I've put a folder named blogs (contains a markdown file)
  3. Each file name will be the same as the blog URL: gettemplate-introduction is gettemplate-introduction.md file
  4. Read each file content using the fs module, file the file name similar to the title and extract the content and render the content using the server-side API

Don't worry if it's too confusing, I'll bring some code samples below, along witha code repository to quickly get started

A few packages we will be needing because we are reading markdown content and parsing it into HTML

npm i remark gray-matter remark-html
Enter fullscreen mode Exit fullscreen mode

Once installed, we are ready. Inside the pages directory, create a folder named blogs and inside it, add [slug].js file

This will serve as a nested route meaning blogs/{name-of-the-blog} will be the served URL

Inside this slug.js file, we need to fetch the content, read the file to get markdown content, convert it into HTML and pass it to React to render it.

import path from "path";
import matter from "gray-matter";
import { remark } from "remark";
import html from "remark-html";
import fs from "fs";
import Head from "next/head";
import SingleBlog from "../../components/SingleBlog";
import { blogs } from "../../content/blogs/blogs";

const blogsDir = path.join(process.cwd(), "content/blogs");

export async function getStaticPaths() {
    const files = fs.readdirSync(blogsDir);
    const paths = files
        .filter((file) => file.endsWith(".md"))
        .map((file) => ({
            params: { slug: file.replace(/\.md$/, "") },
        }));
    return { paths, fallback: false };
}

export async function getStaticProps({ params }) {
    const { slug } = params;
    const filePath = path.join(blogsDir, `${slug}.md`);
    const fileContents = fs.readFileSync(filePath, "utf-8");

    const { data, content } = matter(fileContents);

    const processedContent = await remark().use(html).process(content);
    const contentHtml = processedContent.toString();

    // Find blog metadata from blogs.js
    const blogData = blogs.find((blog) => {
        const blogSlug = blog.title
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, "-")
            .replace(/^-+|-+$/g, "");
        return blogSlug === slug;
    });

    return {
        props: {
            data: data,
            contentHtml: contentHtml,
            blogData: blogData,
            slug: slug,
        },
    };
}

export default function BlogPage({ data, contentHtml, blogData, slug }) {
    const title = blogData?.title || slug.replace(/-/g, " ");
    const description = blogData?.description || "Read our latest blog post";
    const bannerImage = blogData?.banner || "/logo.png";
    const tags = blogData?.tags || [];
    const date = blogData?.date || data?.date;

    return (
        <>

            <SingleBlog data={data} contentHtml={contentHtml} />
        </>
    );
}

Enter fullscreen mode Exit fullscreen mode

Below, you will find how we are rendering SingleBlog content

import React from "react";
import { useRouter } from "next/router";
import { blogs } from "../content/blogs/blogs";
import { ArrowLeft } from "lucide-react";

const SingleBlog = ({ data, contentHtml }) => {
    const router = useRouter();

    const slug = router.query.slug;
    const blogData = blogs.find((blog) => {
        const blogSlug = blog.title
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, "-")
            .replace(/^-+|-+$/g, "");
        return blogSlug === slug;
    });

    return (
        <div className="prose p-4 max-w-7xl mx-auto border-l border-r border-dashed border-zinc-200 min-h-screen">
            <div className="pt-20 max-w-4xl mx-auto">
                <div
                    className="w-fit flex items-center gap-1 mb-8 hover:border border-dashed border-zinc-200 p-1 cursor-pointer"
                    onClick={() => router.push("/blogs")}
                >
                    <ArrowLeft className="w-4 h-4" />
                    <span className="text-zinc-900 hover:text-zinc-700 transition-all text-sm whitespace-nowrap">
                        Blogs
                    </span>
                </div>
                {blogData?.banner && (
                    <img
                        src={blogData.banner}
                        alt={blogData.title}
                        className="w-full h-full border border-zinc-100 border-dashed object-contain rounded-md mb-4"
                    />
                )}
                <h1 className="text-4xl font-bold mb-4">
                    {router.query.slug.replace(/-/g, " ")}
                </h1>
                <p className="text-gray-500 text-sm mb-6">{blogData.description}</p>
                <p className="text-gray-500 text-sm mb-6">{data.date}</p>
                <div className="flex flex-wrap gap-2 my-2">
                    {blogData.tags.map((tag) => (
                        <span
                            key={tag}
                            className="border border-zinc-200 border-dashed text-zinc-800 text-xs px-2 py-1 rounded-full"
                        >
                            {tag}
                        </span>
                    ))}
                </div>
                <div
                    dangerouslySetInnerHTML={{ __html: contentHtml }}
                    className="my-2 [&_p]:my-4 [&_h1]:my-4 [&_h2]:my-4 [&_h3]:my-4 [&_h4]:my-4 [&_h5]:my-4 [&_h6]:my-4 [&_a]:text-black [&_a:hover]:text-black [&_a]:underline [&_a]:cursor-pointer [&_li]:list-disc [&_li]:list-inside"
                />
            </div>
        </div>
    );
};
export default SingleBlog;

Enter fullscreen mode Exit fullscreen mode

In this way, when we open blogs/{name-of-the-blog} URL, Nextjs will run getServerSide, which uses the name-of-the-blog as params to read the exact file content and finally pass it to the render method

Lastly, to manage new blogs, we need to put the entire blog inside one JSON, and I've used the same content directory in the root directory. Inside it, add a file named blogs.json. This json file contains all the blog details as given below

export const blogs = [
    {
        title: "20 Portfolio website templates for developers and designers",
        description:
            "Discover the best portfolio website templates for developers and designers. These templates are built with Next.js and Tailwind CSS and are designed to be responsive and easy to customize.",
        banner:
            "https://b4fcijccdw.ufs.sh/f/mVUSE925dTRYeccOGphRC8d70OUPuc2IwtYrqSm6zavokBX3",
        tags: ["Next.js", "React", "Templates", "Portfolio"],
        date: "October 8, 2025",
    },
    {
        title: "Welcome gettemplate",
        description:
            "Discover the new UI, premium templates, and future roadmaps of GetTemplate.website, your go-to for React and Next.js code templates.",
        banner:
            "https://b4fcijccdw.ufs.sh/f/mVUSE925dTRYEo9UUDgbos21nV4U8LqPX9y0aifKzZCQRjhM",
        tags: ["Next.js", "React", "Templates", "Updates"],
        date: "September 30, 2025",
    },
];

Enter fullscreen mode Exit fullscreen mode

Consider this JSON as our database; one can even migrate it to Firebase, Supabase, or MongoDB later on if you want to scale the application or use some authentication service

One more thing regarding files such as images, for images, you can integrate Firebase or Supabase storage or AWS storage as well, you can even use Uploadthing to upload images and get a public URL and then use as banner image and markdown images

We are using Nextjs server-side API, which has costs, so take care of it if your application scales up to 2k visitors per month.

This is by far the quickest method to create your own content website instantly. This is still not the static content as we are serving content finally using JavaScript API whereas I want to render the final HTML stored in the code repository to make it instant fast and SEO optimsed at the same time.

But for the time being this is good, that's it for today, see you in the next one

Shrey

Top comments (0)