How to Build a Developer Portfolio Website with Next.js
Step-by-step guide to building a professional developer portfolio that actually gets you hired. Learn SEO, performance optimization, and design principles that make your work stand out.
Your portfolio is your most important marketing tool as a developer. It's often the first thing hiring managers see. Here's how to build one that stands out, based on my experience building this portfolio and other production projects.
Why Build Your Own Portfolio?
Using a template is fine, but building your own portfolio:
- Demonstrates your skills - You're a developer, so develop!
- Complete customization - Exactly what you want
- Great learning project - Real-world Next.js experience
- Better SEO - Optimized for your specific keywords
Planning Your Portfolio
Before writing code, answer these questions:
1. Who is Your Audience?
- Hiring managers at tech companies?
- Freelance clients?
- Open source community?
2. What Do You Want to Showcase?
- Projects (most important)
- Skills and technologies
- Writing/blog posts
- Contact information
3. What's Your Personal Brand?
- Clean and minimal?
- Bold and creative?
- Technical and detailed?
Project Structure
Here's the structure I use:
portfolio/
├── src/
│ ├── app/
│ │ ├── page.tsx # Homepage
│ │ ├── layout.tsx # Root layout
│ │ ├── blog/
│ │ │ ├── page.tsx # Blog listing
│ │ │ └── [slug]/
│ │ │ └── page.tsx # Blog post
│ │ ├── api/
│ │ │ └── contact/
│ │ │ └── route.ts # Contact form API
│ │ ├── sitemap.ts # Dynamic sitemap
│ │ └── robots.ts # Robots.txt
│ ├── components/
│ │ ├── Header.tsx
│ │ ├── Footer.tsx
│ │ ├── ProjectCard.tsx
│ │ └── ContactForm.tsx
│ └── lib/
│ └── blog.ts # Blog utilities
├── content/
│ └── blog/ # MDX blog posts
├── public/
│ ├── favicon.svg
│ └── og-image.png
└── tailwind.config.ts
Essential Sections
1. Hero Section
First impressions matter. Be clear about who you are:
function Hero() {
return (
<section className="min-h-screen flex items-center justify-center">
<div className="max-w-4xl mx-auto px-6 text-center">
<h1 className="text-5xl md:text-7xl font-bold text-white mb-6">
Justin Malinow
</h1>
<p className="text-xl md:text-2xl text-gray-400 mb-8">
Full-Stack Developer & Audio Engineer
</p>
<p className="text-lg text-gray-500 max-w-2xl mx-auto">
I build web applications and audio plugins.
Currently focused on fintech and music technology.
</p>
<div className="flex gap-4 justify-center mt-8">
<a href="#projects" className="btn-primary">
View Projects
</a>
<a href="#contact" className="btn-secondary">
Get in Touch
</a>
</div>
</div>
</section>
);
}
2. Projects Section
This is the most important section. Show 4-6 of your best projects:
const projects = [
{
name: 'InsureSignal',
description: 'Insurance data intelligence platform',
tech: ['Next.js', 'PostgreSQL', 'Prisma'],
url: 'https://insuresignal.com',
github: 'https://github.com/...',
},
// ... more projects
];
function Projects() {
return (
<section id="projects" className="py-24">
<h2 className="text-3xl font-bold text-white mb-12">
Featured Projects
</h2>
<div className="grid md:grid-cols-2 gap-8">
{projects.map((project) => (
<ProjectCard key={project.name} project={project} />
))}
</div>
</section>
);
}
Pro tips for project cards:
- Use screenshots or GIFs
- Link to live demo AND GitHub
- List specific technologies used
- Include a brief description of your role
3. Skills Section
Show what you know, but don't list everything:
const skills = {
frontend: ['React', 'Next.js', 'TypeScript', 'Tailwind CSS'],
backend: ['Node.js', 'PostgreSQL', 'Prisma', 'REST APIs'],
tools: ['Git', 'Vercel', 'Figma', 'VS Code'],
};
4. Contact Section
Make it easy to reach you:
function Contact() {
return (
<section id="contact" className="py-24">
<h2 className="text-3xl font-bold text-white mb-8">
Get in Touch
</h2>
<p className="text-gray-400 mb-8">
I'm currently open to new opportunities.
Let's build something together.
</p>
<ContactForm />
<div className="flex gap-6 mt-8">
<a href="https://github.com/yourusername">GitHub</a>
<a href="https://linkedin.com/in/yourusername">LinkedIn</a>
<a href="mailto:you@example.com">Email</a>
</div>
</section>
);
}
SEO Optimization
SEO is crucial for being discovered:
Metadata
// app/layout.tsx
export const metadata: Metadata = {
title: 'Justin Malinow | Full-Stack Developer',
description: 'Full-stack developer specializing in React, Next.js, and TypeScript. View my portfolio of web applications and audio software.',
keywords: ['developer', 'react', 'next.js', 'portfolio'],
openGraph: {
title: 'Justin Malinow | Full-Stack Developer',
description: 'Full-stack developer portfolio',
images: ['/og-image.png'],
},
};
Structured Data (JSON-LD)
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Person',
name: 'Justin Malinow',
jobTitle: 'Full-Stack Developer',
url: 'https://justinmalinow.com',
sameAs: [
'https://github.com/username',
'https://linkedin.com/in/username',
],
};
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
Sitemap
// app/sitemap.ts
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://yoursite.com',
lastModified: new Date(),
priority: 1,
},
{
url: 'https://yoursite.com/blog',
lastModified: new Date(),
priority: 0.8,
},
];
}
Performance Optimization
A slow portfolio reflects poorly on your skills:
Image Optimization
import Image from 'next/image';
<Image
src="/project-screenshot.png"
alt="Project screenshot"
width={800}
height={600}
priority // For above-the-fold images
placeholder="blur"
/>
Font Loading
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
});
Lazy Loading
import dynamic from 'next/dynamic';
const ContactForm = dynamic(() => import('./ContactForm'), {
loading: () => <FormSkeleton />,
});
Design Principles
1. Keep It Simple
Don't overdesign. Clean and readable beats flashy and confusing.
2. Mobile First
Most recruiters check portfolios on mobile first:
/* Start with mobile styles */
.container {
padding: 1rem;
}
/* Then add desktop styles */
@media (min-width: 768px) {
.container {
padding: 2rem;
}
}
3. Consistent Spacing
Use a spacing scale:
// Tailwind spacing: 4, 6, 8, 12, 16, 24
<section className="py-24 px-6">
<h2 className="mb-8">...</h2>
<div className="space-y-6">...</div>
</section>
4. Limit Colors
Pick 2-3 colors and stick with them:
:root {
--bg: #050507;
--text: #ffffff;
--accent: #7c3aed;
--muted: #6b6b80;
}
Common Mistakes to Avoid
1. Too Many Projects
Quality over quantity. 4-6 excellent projects beats 15 mediocre ones.
2. No Live Demos
If I can't see it working, I assume it doesn't work.
3. Walls of Text
Use bullet points, short paragraphs, and visual hierarchy.
4. Broken Links
Test every link before deploying.
5. No Contact Info
Make it easy to reach you. Include email, LinkedIn, GitHub.
Deploying Your Portfolio
I recommend Vercel for Next.js:
- Push to GitHub
- Connect repo to Vercel
- Configure custom domain
- Enable analytics
# Deploy with Vercel CLI
npm i -g vercel
vercel
Analytics
Track who's visiting:
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
);
}
Conclusion
Your portfolio is a living document. Update it regularly:
- Add new projects
- Write blog posts
- Update your skills
- Improve performance
The best portfolio is one that accurately represents your skills and gets you opportunities.
This site you're reading is my portfolio, built with exactly these principles. Check out the projects section and browse more blog posts for inspiration.
Now go build yours!
Related Articles: