<- Back to blog

How I Built My Personal Website with Next.js

A look at the tools and decisions behind my personal portfolio website.

By Han Jo Xi

My personal website is where I bring together my interests in software engineering, artificial intelligence, data analytics and the web. I wanted it to be more than a page with my name on it, so I built it myself and used the project to learn how the pieces of a modern website fit together.

How I Built My Personal Website with Next.js

Choosing the stack

I chose Next.js because it gives me a practical foundation for a content-focused website without making me assemble every part of the application from scratch. It handles routing, static generation and metadata in the same project, which makes it a good fit for a portfolio that needs both a fast homepage and a small blog.

The project uses React for its component model and TypeScript for safer interfaces between the different parts of the site. TypeScript is especially useful as the portfolio grows because it makes the shape of portfolio data and blog metadata explicit. If I change a field or add a new content type, the compiler can help me find the places that need updating.

Styling the interface

I use Tailwind CSS alongside regular CSS in app/globals.css. Tailwind keeps layout utilities close to the markup, while the global stylesheet holds the site's visual language, reusable classes and custom properties. This gives me a quick way to adjust a component without losing control over the details that make the portfolio feel like one coherent site.

The visual direction is deliberately technical and restrained. Monospace labels, small section markers and a limited color system give the page a sense of structure without competing with the content. I want the design to support the work rather than become the main subject of the website.

Writing with MDX

The blog is written in MDX. Each article is a file in content/blog that exports its metadata and contains normal Markdown with the option to use React components. This keeps writing simple while leaving room for richer content later, such as screenshots, diagrams or interactive examples.

The posts are registered in one place in lib/blog.ts. From that registry, the blog index can list every article and Next.js can generate a static route for each slug. Adding a post therefore stays straightforward: write the MDX file, add its metadata to the registry and build the site.

Deploying the site

I deploy the website with Vercel. That matches the project well because the site is built with Next.js, and Vercel can build and serve the application directly from the repository. It also gives me a simple workflow for publishing updates: make a change, verify the build, and deploy the new version.

I use Cloudflare for the domain layer. Keeping the domain and the application deployment as separate concerns makes the setup easier to reason about. Vercel serves the website, while Cloudflare manages the domain and its DNS configuration.

Making the site discoverable

SEO was part of the implementation rather than something I wanted to add at the very end. Each blog post provides a title, description, date, author and tags. Next.js uses that information to generate the page metadata, including the canonical URL and social sharing details when an article has an image.

The site also generates a sitemap from the same blog registry. That means a newly registered article is included in the sitemap automatically, so the list of discoverable pages stays connected to the pages that actually exist.

Each article page includes BlogPosting JSON-LD. It describes the post to search engines with its headline, description, publication date, author and canonical page URL. Structured data does not replace useful writing, but it gives the page a clearer machine-readable identity.

Handling images

For posts that need images, the site has a reusable BlogImage component built around Next.js image handling. It keeps image dimensions and alternative text explicit, which helps prevent layout shifts and makes the content more accessible. Next.js can then optimize the delivered image for the page instead of treating it as an unstructured file.

This article is intentionally text-only for now. The content system supports adding a cover image later without requiring the article or the rest of the blog architecture to be redesigned.

Why this stack works for me

This stack gives me a useful balance between control and convenience. Next.js provides the application structure and static generation, React keeps the UI modular, TypeScript makes the data safer to change, and Tailwind CSS makes layout work efficient. MDX lets the blog stay close to the way I write, while Vercel and Cloudflare keep deployment and domain management simple.

Most importantly, the website is itself a small example of the kind of work I enjoy: taking separate systems, understanding how they connect and turning them into something people can use. I can keep improving the design and adding content while the foundation remains understandable.