Personal Portfolio Website with Next.js
Create a modern developer portfolio using Next.js with fast pages and clean UI components
Time to implement the project: ~ 10-16 hours
- Next.js
- React Fundamentals
- File-Based Routing
- Static Generation
- Component Layouts
- Chakra UI
In this beginner-level Next.js project, you will build a personal portfolio website that presents your work, skills, and developer background in a structured and professional layout. The site should include essential sections such as an introduction, project showcase, skills overview, and contact information. Each page should be generated using Next.js routing so navigation remains fast and predictable.
The portfolio must demonstrate clean component structure and modern frontend practices. You will use Chakra UI to design the interface with responsive layouts, reusable components, and accessible styling. The focus is not only on visual presentation but also on building a maintainable project structure that reflects how real Next.js applications are organized.
What You Will Practice in This Project
This project introduces how Next.js simplifies building structured websites using React. Instead of manually configuring routing or page structure, Next.js uses a file-based routing system where each file automatically becomes a page. This approach encourages clear organization and predictable navigation patterns.
Creating a portfolio site also teaches an important professional skill: presenting your work effectively. You will learn how to structure information so visitors can quickly understand what you build, what technologies you use, and what problems you solve.
Prerequisites Before Starting
This project assumes basic knowledge of React and the ability to run a Next.js application locally. The task focuses on page structure, layout components, and understanding how Next.js handles routing and rendering.
- Basic React concepts such as components and props
- Understanding how Next.js pages and folders define routes
- Familiarity with JSX and simple UI composition
- Basic usage of Chakra UI layout and typography components
- Comfort working with project folders and file structure
Key Functional Requirements
A well-built portfolio should feel fast, readable, and logically structured. Visitors should be able to navigate through sections easily and quickly understand the developer behind the site. These requirements emphasize clarity and structure rather than complicated animations or visual effects.
| Requirement | Explanation |
| Multiple pages using Next.js routing | Separate pages such as Home, Projects, and Contact demonstrate how file-based routing works in Next.js. |
| Reusable layout components | Shared elements like navigation bars and footers should be implemented as reusable components. |
| Project showcase section | Displaying projects helps visitors quickly evaluate your development experience. |
| Responsive design | The layout must adapt to mobile, tablet, and desktop screens. |
| Accessible UI with Chakra components | Using Chakra UI ensures consistent styling and accessible default behavior. |
| Clean project structure | Pages, components, and assets should be organized logically. |
| Static page generation | Using Next.js static generation improves performance and page load speed. |
| Navigation between sections | Users should move between pages without losing context or performance. |
| Contact section | A simple contact area allows visitors to reach out or view social links. |
Implementation Tips for a Clean Next.js Portfolio
Start by defining the core pages of the portfolio before building components. Create a clear folder structure that separates pages, reusable components, and assets. Next.js makes it easy to keep routing predictable, so focus on component reusability and page layout consistency. Chakra UI can help establish visual consistency quickly through layout primitives such as Stack, Box, and Grid. A simple structure built well is more valuable than a complex site built poorly.
- Use a shared layout component to keep navigation consistent across pages
- Organize project cards as reusable components
- Use Chakra UI grid layouts to maintain responsive structure
- Keep text content concise and readable
- Optimize images so the site loads quickly
- Structure project data so new projects can be added easily
Common Mistakes When Building a Personal Portfolio Website with Next.js
1. Hardcoding every project card directly inside the page
A portfolio usually starts with three or four projects, so beginners often write every project card manually inside the page component. This seems harmless, but it quickly becomes difficult to maintain. When you add more projects, change card layout, update links, or add filtering later, you have to edit repeated JSX in many places.
Problematic approach:
export default function ProjectsPage() {
return (
<section>
<div className="project-card">
<h2>Weather App</h2>
<p>React, API, CSS</p>
<a href="https://github.com/example/weather">GitHub</a>
</div>
<div className="project-card">
<h2>Movie Search</h2>
<p>Next.js, API, UI</p>
<a href="https://github.com/example/movies">GitHub</a>
</div>
</section>
);
}
This repeats the same structure for every project. If you later add a live demo link, image, category, or featured badge, every card must be edited manually.
Better approach:
export const projects = [
{
title: "Weather App",
description: "A small weather dashboard using an external API.",
stack: ["React", "API", "CSS"],
githubUrl: "https://github.com/example/weather",
demoUrl: "https://weather.example.com",
image: "/projects/weather-app.png"
},
{
title: "Movie Search",
description: "A movie search interface with filters and detail views.",
stack: ["Next.js", "API", "UI"],
githubUrl: "https://github.com/example/movies",
demoUrl: "https://movies.example.com",
image: "/projects/movie-search.png"
}
];
Reusable component example:
function ProjectCard({ project }) {
return (
<article>
<h2>{project.title}</h2>
<p>{project.description}</p>
<ul>
{project.stack.map((technology) => (
<li key={technology}>{technology}</li>
))}
</ul>
<a href={project.githubUrl} target="_blank" rel="noreferrer">
View code
</a>
</article>
);
}
export default function ProjectsPage() {
return (
<section>
{projects.map((project) => (
<ProjectCard key={project.title} project={project} />
))}
</section>
);
}
Pay attention to: Store portfolio content as structured data. Then render it through reusable components. This makes the site easier to update when your portfolio grows.
2. Creating navigation with normal anchor tags for internal pages
Next.js provides routing tools for a reason. A common mistake is using normal <a> tags for internal navigation between Home, Projects, About, and
Contact pages. The links may still work, but they can trigger full page reloads instead of using Next.js client-side navigation.
Problematic code:
function Navbar() {
return (
<nav>
<a href="/">Home</a>
<a href="/projects">Projects</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
);
}
This does not use the routing behavior that makes Next.js navigation feel fast and app-like.
Better approach:
import Link from "next/link";
const navItems = [
{ label: "Home", href: "/" },
{ label: "Projects", href: "/projects" },
{ label: "About", href: "/about" },
{ label: "Contact", href: "/contact" }
];
function Navbar() {
return (
<nav aria-label="Main navigation">
{navItems.map((item) => (
<Link key={item.href} href={item.href}>
{item.label}
</Link>
))}
</nav>
);
}
External links are different:
<a
href="https://github.com/yourname"
target="_blank"
rel="noreferrer"
>
GitHub
</a>
Pay attention to: Use next/link for internal routes and regular anchor tags for external links. This keeps navigation fast, semantic, and
predictable.
3. Uploading large project screenshots without optimization
Portfolio sites rely heavily on screenshots. Large unoptimized images can make an otherwise simple Next.js site feel slow. Beginners often export screenshots at huge
sizes, place them directly in the page, and render them with ordinary <img> tags. This can hurt loading speed and make the first impression weaker.
Problematic approach:
function ProjectPreview() {
return (
<img
src="/projects/dashboard-full-size.png"
alt="Dashboard project"
/>
);
}
This image may be too large, may shift layout while loading, and does not take advantage of Next.js image tooling.
Better approach:
import Image from "next/image";
function ProjectPreview() {
return (
<Image
src="/projects/dashboard-preview.png"
alt="Screenshot of analytics dashboard project"
width={960}
height={540}
priority={false}
/>
);
}
Image data example:
export const projects = [
{
title: "Analytics Dashboard",
image: {
src: "/projects/analytics-dashboard.png",
alt: "Analytics dashboard with charts and summary cards",
width: 960,
height: 540
}
}
];
Rendering from data:
<Image
src={project.image.src}
alt={project.image.alt}
width={project.image.width}
height={project.image.height}
/>
Pay attention to: Use compressed images, meaningful alt text, and explicit dimensions. Portfolio screenshots should support your work, not slow the page down.
4. Forgetting metadata, page titles, and social preview content
A portfolio is meant to be shared with recruiters, clients, and other developers. If every page has the same generic title or no description, shared links look unfinished. Next.js gives you tools for metadata, but beginners often focus only on the visual page and forget the document-level information.
Problematic result:
<title>Create Next App</title>
This tells visitors nothing about you, your role, or the purpose of the page.
Better approach in the App Router:
export const metadata = {
title: "Katerina Pidan | Frontend Developer Portfolio",
description:
"Frontend developer portfolio with Next.js projects, UI work, and contact information.",
openGraph: {
title: "Katerina Pidan | Frontend Developer Portfolio",
description:
"Explore frontend projects, skills, and contact information.",
type: "website",
images: [
{
url: "/og-image.png",
width: 1200,
height: 630,
alt: "Frontend developer portfolio preview"
}
]
}
};
Page-specific metadata example:
export const metadata = {
title: "Projects | Frontend Developer Portfolio",
description:
"Selected frontend projects built with React, Next.js, APIs, and responsive UI patterns."
};
Pay attention to: Add clear titles and descriptions for important pages. Your portfolio is not only a website; it is also a link people will preview in messages, emails, and social platforms.
5. Designing only for desktop and fixing mobile at the end
Portfolio websites are often viewed on phones first: from LinkedIn, Telegram, email, or a recruiter’s mobile browser. A common mistake is designing a large desktop hero section and project grid first, then trying to patch mobile layout with random overrides. This usually creates cramped text, broken navigation, and project cards that are difficult to scan.
Problematic layout:
.hero {
width: 1200px;
display: flex;
gap: 80px;
}
.projects {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
Fixed widths and desktop-first grids can break on smaller screens.
Better responsive structure:
.hero {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 32px 16px;
display: grid;
gap: 32px;
}
@media (min-width: 768px) {
.hero {
grid-template-columns: 1fr 1fr;
padding: 64px 24px;
}
}
.projects {
display: grid;
grid-template-columns: 1fr;
gap: 24px;
}
@media (min-width: 768px) {
.projects {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.projects {
grid-template-columns: repeat(3, 1fr);
}
}
Chakra UI-style layout idea:
<SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={6}>
{projects.map((project) => (
<ProjectCard key={project.title} project={project} />
))}
</SimpleGrid>
Pay attention to: Build mobile-friendly structure from the beginning. Hero text, navigation, project cards, and contact links should remain readable before you add desktop polish.
By completing this project, you'll gain practical experience building a structured website with Next.js, organizing pages through file-based routing, and creating reusable UI components using Chakra UI. This portfolio project serves as both a learning exercise and a real asset you can showcase to potential employers, helping you demonstrate your ability to build modern web interfaces using the Next.js framework.
Reference Implementations Worth Studying
Beginner-friendly portfolio template:
omidzed - Next.js Tailwind Portfolio
This is the most direct reference for the beginner version of the project. It is a simple multipage and responsive portfolio theme built with Next.js, React, and Tailwind CSS. It includes reusable components, project filtering by category, project search, dark mode, smooth scrolling, dynamic forms, a back-to-top button, and a download file button.
Pay particular attention to:
- How portfolio content is separated into folders such as components, data, hooks, pages, public assets, and styles.
- How reusable components make the site easier to maintain than one large page file.
- How project filtering and search can make a portfolio more useful when the project list grows.
- How dark mode and responsive layout improve the overall presentation without changing the core content.
- How a simple portfolio can still include professional touches such as smooth scroll and downloadable files.
Use this repository as a practical baseline. Even if your project uses Chakra UI instead of Tailwind CSS, the structure is useful for learning how to organize a clean multipage Next.js portfolio.
More complete portfolio and blog system:
kirkwat - Portfolio Blog
This implementation is more advanced because it combines a personal portfolio with a blog and a content editing workflow. It uses Next.js for the frontend and Sanity for content management. The project includes editable projects, a native Sanity Studio, real-time collaborative editing, side-by-side previews, block content support, and webhook-triggered incremental static regeneration.
When studying the code, focus on:
- How a portfolio changes when content is managed through a CMS instead of hardcoded arrays.
- How projects, blog posts, schemas, API helpers, and preview mode are separated into clear folders.
- How static generation and content updates can work together in a real publishing workflow.
- How TypeScript and Tailwind CSS support a larger portfolio/blog codebase.
- How an authoring environment can make the site easier to update after deployment.
Use this repository as a reference for where your portfolio can go after the beginner version. You do not need a CMS for the first build, but studying this project helps you understand scalable content architecture.
Alternative GitHub Pages deployment reference:
joeoverflowcode - Next Portfolio
This repository is useful as an alternative implementation because it focuses on a modern Next.js portfolio deployed to GitHub Pages. It uses Next.js, TypeScript,
Tailwind CSS, Aceternity UI components, and a static export configuration. The repository also shows important deployment details such as output: "export",
basePath, and disabling server-based image optimization for static export.
While reviewing this project, examine:
- How the App Router structure differs from older pages-based portfolio examples.
- How modern UI components and Tailwind CSS can create a more animated portfolio presentation.
- How GitHub Pages deployment affects Next.js configuration.
- Why static export requires careful handling of base paths and image optimization.
- How deployment constraints should influence technical choices before you build the whole site.
This is a valuable comparison point if you want to host your portfolio for free on GitHub Pages. Study the deployment configuration carefully, because a portfolio that works locally but fails after deployment is not portfolio-ready.