Why I Built My Portfolio with Next.js 16 Instead of a Template
The case for building your developer portfolio from scratch — custom animations, a proper design system, and what I learned moving from mobile to web development.
Every developer needs a portfolio. Most grab a template, swap in their name, and ship it. I spent three weeks building mine from scratch with Next.js 16, React 19, TypeScript, and Framer Motion. Was it worth the extra time? Absolutely — but not for the reasons you'd think.
The Template Problem
I tried templates first. I downloaded four of them. They all looked polished until I started customizing. Want to change the color scheme? That's 47 CSS variables scattered across 12 files. Want to add a section? Hope you understand the template author's component architecture. Want dark mode? The template supports it but the implementation is a mess of Tailwind dark: prefixes with hardcoded fallbacks.
The deeper problem with templates is that they're generic by design. A developer portfolio should reflect how you think about software. If you're showcasing your skills as a frontend developer and your portfolio runs on someone else's code, that's a contradiction.
The Animation System
The most rewarding part of building from scratch was the animation system. I created 15 reusable animation components with Framer Motion: text reveals that stagger word by word, cards that tilt toward your cursor in 3D, a custom cursor with trailing dots, SVG icons that draw themselves on scroll, spotlight cards with radial gradients that follow your mouse.
Each component is self-contained and configurable. SlideUp accepts a direction prop (up, down, left, right) and an optional scale. TextReveal can split by word or character with different animation styles. StaggerContainer wraps any list and automatically staggers its children's entrance.
The key technical decision was respecting prefers-reduced-motion in every single component. Roughly 10% of users have this setting enabled due to vestibular disorders or motion sensitivity. Each animation component checks useReducedMotion() from Framer Motion and either renders a static fallback or skips the animation entirely. This isn't optional polish — it's accessibility compliance.
Dark Mode Done Right
Most dark mode implementations use Tailwind's dark: prefix, which requires doubling every color class: bg-white dark:bg-gray-900 text-black dark:text-white. My approach uses CSS custom properties instead.
I defined semantic color tokens as CSS variables: --bg, --surface, --card, --text, --text-muted, --accent, --border. Light and dark themes set different values for the same variables. In my components, I use bg-bg, text-text, border-border — single classes that automatically adapt to the active theme.
This approach has three benefits. First, adding a new color only requires defining it in two places (light and dark values) instead of finding every component that uses it. Second, theme transitions are smooth — a single CSS transition: color 0.3s, background-color 0.3s on the root element animates the entire page. Third, the HTML stays clean — no dark: prefix clutter.
Tailwind CSS 4 made this even cleaner with the @theme inline directive. I registered all my CSS variables as Tailwind tokens, so I get autocomplete, type safety, and purging — with the flexibility of CSS custom properties.
What Next.js 16 Gives You
I chose Next.js for three reasons: static site generation, file-based routing, and the React Server Components model.
Static generation means my portfolio compiles to plain HTML at build time. No server needed, no cold starts, no loading spinners. The blog posts, project pages, and case studies are all pre-rendered. The result is a Lighthouse performance score that's as high as it gets.
Server Components let me keep heavy logic (data fetching, metadata generation) on the server while only shipping interactive code to the client. My project listing page is a server component — it reads the project data and renders HTML. The animation wrappers are client components that hydrate on top. This split keeps the JavaScript bundle small.
File-based routing means adding a new page is creating a new folder. No router configuration, no route definitions. The app/blog/[slug]/page.tsx file automatically creates dynamic routes for every blog post. Combined with generateStaticParams(), each post gets its own pre-rendered HTML page.
The Design System
Instead of picking colors randomly, I built a coherent system. Five accent colors — blue (primary), purple (secondary), amber (warm), orange (coral), and emerald (green) — each with hover variants for both light and dark mode. Each section of the homepage uses a different accent color, creating visual rhythm without looking chaotic.
The typography uses the system font stack — no custom font downloads. Spacing follows a 4px grid. Border radius is consistent across cards, buttons, and badges. Shadows adapt to the theme. Every component respects the same design tokens.
This consistency is what separates a custom portfolio from a template. Templates have good design in isolation, but the parts don't always fit together. When you build the system yourself, every piece is intentional.
What I Learned Moving from Mobile to Web
Coming from Android development, web development felt simultaneously easier and harder. Easier because the feedback loop is instant — save a file and the browser updates. No build times, no emulator startup. CSS is more expressive than XML layouts. Hot module replacement means you never lose state while iterating on UI.
Harder because the web platform has more footguns. There's no single architecture pattern like MVVM — you choose between dozens of state management approaches. Server-side rendering adds complexity that doesn't exist in mobile. CSS specificity, hydration mismatches, and layout shifts are entire categories of bugs that Android developers never deal with.
The biggest adjustment was thinking in components instead of screens. In Android, you build a screen and it owns its entire lifecycle. In React, you build small composable pieces and assemble them. It's more flexible but requires more upfront design thinking.
Was It Worth Three Weeks?
Three weeks for a portfolio sounds excessive. But consider what I got: a site I understand completely, an animation library I can reuse in client projects, deep knowledge of Next.js and React, and a portfolio that actually demonstrates the skills it claims to showcase.
The template approach would have saved two weeks and given me a site that looks like every other developer portfolio. The custom approach took longer but produced something that's genuinely mine — and taught me more about web development than any tutorial could.
If you're deciding between a template and custom: build it yourself if you have the time. The skills you gain building a portfolio are the same skills clients will pay you for. Just don't let perfectionism stop you from shipping — my first version had half the animations and a simpler design. I iterated from there.