Hello World: Building a Modern Portfolio with Next.js

1 min read
nextjsreactportfoliomdx

Why Build a Custom Portfolio?

Every developer needs a home on the web. While template-based solutions get you online fast, building from scratch gives you full control over performance, design, and the technology stack.

This site is built with Next.js 14 using the App Router, Tailwind CSS for styling, and Velite for content management. The entire site is statically exported and deployed to Cloudflare Pages for near-instant load times.

The Tech Stack

Here is a quick overview of the core dependencies and why each was chosen:

  • Next.js 14 -- App Router with static export for zero-runtime overhead
  • TypeScript -- strict mode for type safety across the entire codebase
  • Tailwind CSS -- utility-first styling with dark mode support baked in
  • Framer Motion -- smooth, declarative animations and scroll-triggered reveals
  • Velite + MDX -- type-safe content pipeline with Zod schema validation

Code Highlighting with Shiki

One of the key features of this blog setup is syntax highlighting powered by Shiki. It supports dual themes that automatically match the site's light and dark mode toggle.

Here is a simple React component as an example:

interface GreetingProps {
  name: string;
  role?: string;
}

export const Greeting = ({ name, role = "developer" }: GreetingProps) => {
  return (
    <div className="rounded-lg bg-white p-6 dark:bg-slate-900">
      <h2 className="text-xl font-bold">Hello, {name}!</h2>
      <p className="mt-2 text-gray-600 dark:text-gray-400">
        Welcome, fellow {role}.
      </p>
    </div>
  );
};

And a quick bash snippet showing the development workflow:

# Start the development server inside Docker
docker compose up

# Open the site at http://localhost:3000
# Edit files and see changes via hot reload

Content Pipeline

Blog posts are written in MDX files inside content/blog/. Velite validates the frontmatter against a Zod schema at build time, compiles the MDX to optimized React components, and outputs typed modules that Next.js can import directly.

This means content errors are caught at build time, not at runtime. A missing title or malformed date will fail the build with a clear error message.

{
  "title": "Post titles are validated at build time",
  "maxLength": 120,
  "required": true
}

What is Next

Future posts will cover the responsive grid layout system, the animation architecture, and how the Sanity CMS integration works for quick posts. Stay tuned.