How I Built a Blog & Personal Site with Next.js (Free Hosting!)

Cover Image for How I Built a Blog & Personal Site with Next.js (Free Hosting!)

Hi! I decided to create my own blog and personal site. For the blog, I used a ready-made template and customized it, while I built the personal page from scratch. In this article, I’ll share how I chose the template, modified it, designed the personal page, and developed the site. If you’re looking to create something similar, you might find some useful ideas here.

Screenshot Website

Screenshot Website

Choosing a Template

When I started working on the blog, I realized that building it from scratch would take too long. So, I decided to look for ready-made Next.js templates. Their catalog offers solutions for blogs, online stores, personal pages, and even AI startup websites.

Screenshot Next.js

Among the blog templates, I chose one that doesn’t require a database. I wanted the site to be fully static, allowing me to host it on GitHub Pages, which doesn’t support databases.

My goal was to create a free blog without infrastructure costs. That’s why I selected a template that uses Next.js static generation and Markdown files as the data source. This approach makes it easy to add new articles while keeping everything server-free.

Setting Up the Project

To get started, create the project using the command line:

npx create-next-app --example blog-starter blog-starter-app

After that, simply replace the template’s sample articles with your own.

Screenshot code

The final step is setting up GitHub Pages for the repository and linking a CNAME to the desired domain.

Designing the Personal Page

The blog was ready, but I also needed a personal page to showcase information about myself and my projects.

Figma Community offers plenty of free and paid templates for various projects. The key is to check the license. I was looking for Free to use options, but in the end, I simply took inspiration from existing designs and created my own.

Screenshot Figma

Development and Implementation

For development, I used Next.js and Tailwind CSS.

  • Next.js was a great fit since I integrated the personal page into the existing blog template.
  • I chose Tailwind CSS for its convenience—it allows you to style elements directly in the code without needing separate CSS files. This speeds up development and keeps the code clean.

Thanks to this approach, the implementation took very little time.

Here’s an example of how the header is structured:

const Header: React.FC<{ opacity?: boolean }> = ({ opacity }) => {
  const { asPath, locale } = useRouter();
  const { t } = useTranslation();

  return (
    <>
      <header
        className={cn(
          "sticky top-0 w-full specm:h-[54px] h-[36px] z-10 font-montserrat font-semibold text-[14px] flex items-center border-b border-neutral-200",
          opacity ? "bg-[#ffffff99]" : "bg-white"
        )}
      >
        <SpecContainer className="flex justify-between">
          <Link href="/" locale={locale} className="font-[arial-black]">
            PROGOSLING
          </Link>
          <div className="flex specm:gap-[40px] gap-[10px]">
            <Link
              href="/"
              locale={locale}
              aria-disabled={asPath === "/"}
              className="aria-disabled:opacity-50 aria-disabled:pointer-events-none hover:underline"
            >
              {t("common:header.home")}
            </Link>
            <Link
              href="/blog"
              locale={locale}
              aria-disabled={asPath === "/blog"}
              className="aria-disabled:opacity-50 aria-disabled:pointer-events-none hover:underline"
            >
              {t("common:header.blog")}
            </Link>
            <Link locale={locale} href="/#projects" className="hover:underline">
              {t("common:header.projects")}
            </Link>
          </div>
          <div className="specm:block hidden" />
        </SpecContainer>
      </header>
      <div className="bg-[#381DDB] font-raleway font-bold text-[12px] text-white">
        <SpecContainer className="flex justify-between items-center">
          <nav className="flex gap-2 items-center">
            {locales.map(({ id, name }, i) => (
              <React.Fragment key={id}>
                {i > 0 && <span>•</span>}
                <Link
                  href={asPath}
                  locale={id}
                  aria-disabled={locale === id}
                  className="block py-[6px] aria-disabled:opacity-50 aria-disabled:pointer-events-none"
                >
                  {name}
                </Link>
              </React.Fragment>
            ))}
          </nav>
          <ul className="flex specm:gap-2">
            {social.map(({ element, id }) => (
              <li key={id}>{element}</li>
            ))}
          </ul>
          <a href="mailto:[email protected]" className="group">
            {email}
          </a>
        </SpecContainer>
      </div>
    </>
  );
};

And here’s how the main page looks:

export default function Index({
  allPosts,
  postsCount,
}: {
  allPosts: Post[];
  postsCount: number;
}) {
  const { locale } = useRouter();
  const { t } = useTranslation();

  return (
    <>
      <Layout
        title={t("index-page:meta.title")}
        description={t("index-page:meta.description")}
        header={<Header />}
      >
        <div className="mt-[3rem]" />
        <Main />
        <div className="w-full specl:my-[6.25rem] specm:my-[5rem] my-[40px] border-b border-neutral-200" />
        <AboutMe />
        <LastProjects />
        <Blog allPosts={allPosts} postsCount={postsCount} />
      </Layout>
    </>
  );
}

Final Thoughts

In the end, I achieved:

A fully static blog — no backend, no database.
A personal page — integrated into the project and customized to match my style.
Free hosting — deployed on GitHub Pages.

Now, I can add content and continue developing the project.

If you’re thinking about creating your own site, try a similar approach — use ready-made solutions and adapt them to your needs.