Building a Stunning Portfolio Website with Next.js and Tailwind CSS

In the digital age, a compelling online portfolio is essential for showcasing your work, skills, and personality. Whether you’re a designer, developer, writer, or any creative professional, a well-crafted portfolio website can be your most valuable asset. This tutorial will guide you through building a modern, responsive, and visually appealing portfolio website using Next.js and Tailwind CSS. We’ll cover everything from setting up your development environment to deploying your website, ensuring you have a solid foundation to present your best work to the world.

Why Next.js and Tailwind CSS?

Choosing the right tools is crucial for a successful project. Next.js and Tailwind CSS are an excellent combination for building portfolio websites for several compelling reasons:

  • Next.js: A React framework that offers server-side rendering (SSR), static site generation (SSG), and many other features that enhance performance and SEO. Its file-system-based routing makes navigation simple, and its built-in optimizations ensure a fast and efficient website.
  • Tailwind CSS: A utility-first CSS framework that provides a vast library of pre-defined classes. This approach allows you to quickly style your website without writing custom CSS, leading to faster development and a consistent design.

By using Next.js, we can optimize the website for speed and SEO, ensuring that your portfolio is easily discoverable by potential clients or employers. Tailwind CSS simplifies the styling process, allowing you to focus on content and functionality.

Prerequisites

Before we begin, make sure you have the following installed on your system:

  • Node.js and npm (or yarn): You’ll need Node.js (version 14 or higher) and npm (or yarn) to manage your project dependencies.
  • A code editor: Choose a code editor like VS Code, Sublime Text, or Atom.
  • Basic knowledge of HTML, CSS, and JavaScript: Familiarity with these languages is essential for understanding the code and making modifications.

Setting Up Your Next.js Project

Let’s start by creating a new Next.js project. Open your terminal and run the following command:

npx create-next-app portfolio-website
cd portfolio-website

This command creates a new Next.js project named “portfolio-website” and navigates you into the project directory. Next.js will ask a few questions, you can answer them as you like.

Installing Tailwind CSS

Next, we need to install Tailwind CSS and its related dependencies. Run the following command in your project directory:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

This command installs Tailwind CSS, PostCSS, and Autoprefixer as development dependencies and generates the `tailwind.config.js` and `postcss.config.js` files.

Now, configure Tailwind CSS by adding the paths to all of your template files in your `tailwind.config.js` file. Replace the `content` array with the following:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}',
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',

    // Or if using `src` directory:
    './src/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {
      // Add custom styles here
    },
  },
  plugins: [],
}

Next, import Tailwind’s styles into your global CSS file (usually `src/app/globals.css` or `styles/globals.css`). Add the following directives at the top of the file:

@tailwind base;
@tailwind components;
@tailwind utilities;

Project Structure

Before we start building our portfolio, let’s establish a clear project structure. This will help organize our code and make it easier to maintain.

Here’s a suggested structure:


portfolio-website/
├── pages/
│   ├── index.js          // Home page
│   └── [slug].js         // Dynamic project pages
├── components/
│   ├── Navbar.js         // Navigation bar
│   ├── Footer.js         // Footer
│   ├── ProjectCard.js    // Project card component
│   └── ...               // Other reusable components
├── public/
│   ├── images/
│   │   └── ...           // Images
│   └── ...               // Other static assets
├── styles/
│   └── globals.css       // Global styles
├── tailwind.config.js    // Tailwind configuration
├── postcss.config.js     // PostCSS configuration
└── next.config.js        // Next.js configuration

Building the Navbar Component

Let’s start by creating the navigation bar. Create a new file named `Navbar.js` inside the `components` directory. This component will contain our site’s navigation links.

// components/Navbar.js
import Link from 'next/link';

const Navbar = () => {
  return (
    <nav>
      <div>
        
          [Your Name]
        
        <ul>
          <li>
            About
          </li>
          <li>
            Projects
          </li>
          <li>
            Contact
          </li>
        </ul>
      </div>
    </nav>
  );
};

export default Navbar;

In this code:

  • We import the `Link` component from `next/link` for client-side navigation.
  • We use Tailwind CSS classes to style the navbar, including the background color, shadow, padding, and text styles.
  • The `container mx-auto px-4` classes are used for responsive design, ensuring that the content is centered and has appropriate padding on different screen sizes.

Now, let’s include the Navbar in our main layout. Open `app/layout.js` (or the equivalent file in your project) and import and use the `Navbar` component.

// app/layout.js
import './globals.css'
import { Inter } from 'next/font/google'
import Navbar from '../components/Navbar'

const inter = Inter({ subsets: ['latin'] })

export const metadata = {
  title: '[Your Name] - Portfolio',
  description: 'A portfolio website showcasing [Your Skills] and projects.',
}

export default function RootLayout({ children }) {
  return (
    
      
        
        <main>
          {children}
        </main>
      
    
  )
}

Creating the Footer Component

Next, let’s create a footer component. Create a file named `Footer.js` inside the `components` directory:


// components/Footer.js
const Footer = () => {
  return (
    <footer>
      <div>
        © {new Date().getFullYear()} [Your Name]. All rights reserved.
      </div>
    </footer>
  );
};

export default Footer;

This simple footer displays a copyright notice. We’ll add it to our layout file as well, to ensure it appears on every page.

Update your `app/layout.js` to include the footer:


// app/layout.js
import './globals.css'
import { Inter } from 'next/font/google'
import Navbar from '../components/Navbar'
import Footer from '../components/Footer'

const inter = Inter({ subsets: ['latin'] })

export const metadata = {
  title: '[Your Name] - Portfolio',
  description: 'A portfolio website showcasing [Your Skills] and projects.',
}

export default function RootLayout({ children }) {
  return (
    
      
        
        <main>
          {children}
        </main>
        <Footer />
      
    
  )
}

Building the Home Page (index.js)

Now, let’s create the content for our home page. Open `app/page.js` (or the equivalent file in your project) and add the following code:


// app/page.js

export default function Home() {
  return (
    <div>
      <section>
        <div>
          <h1>Hi, I'm [Your Name]</h1>
          <p>[Your Profession/Title]. [A brief description about yourself and what you do.]</p>
        </div>
      </section>

      <section>
        <div>
          <h2>Featured Projects</h2>
          {/* Add project cards here, we'll create the component next */} 
        </div>
      </section>
    </div>
  );
}

This code creates a basic home page with a welcome message and a section for featured projects. We’ll populate the “Featured Projects” section later with project cards.

Creating the Project Card Component

To display our projects, we’ll create a reusable `ProjectCard` component. Create a file named `ProjectCard.js` inside the `components` directory:


// components/ProjectCard.js
import Image from 'next/image';

const ProjectCard = ({ project }) => {
  return (
    <div>
      
      <div>
        <h3>{project.title}</h3>
        <p>{project.description}</p>
        <a href="{project.link}" target="_blank" rel="noopener noreferrer">View Project</a>
      </div>
    </div>
  );
};

export default ProjectCard;

This component takes a `project` prop, which should be an object containing project details like title, description, image URL, and a link to the project.

  • We use the `Image` component from Next.js for optimized image loading.
  • Tailwind CSS classes are used for styling the card, including the shadow, rounded corners, padding, and text styles.

Populating the Home Page with Project Cards

Now, let’s populate the home page with our `ProjectCard` components. First, let’s create an array of project objects.


// app/page.js
import ProjectCard from '../components/ProjectCard';

const projects = [
  {
    title: 'Project 1',
    description: 'A brief description of Project 1.',
    imageUrl: '/images/project1.jpg', // Replace with your image path
    link: 'https://example.com/project1',
  },
  {
    title: 'Project 2',
    description: 'A brief description of Project 2.',
    imageUrl: '/images/project2.jpg', // Replace with your image path
    link: 'https://example.com/project2',
  },
  // Add more projects as needed
];

export default function Home() {
  return (
    <div>
      {/* ... (previous code) ... */}
      <section>
        <div>
          <h2>Featured Projects</h2>
          <div>
            {projects.map((project, index) => (
              
            ))}
          </div>
        </div>
      </section>
    </div>
  );
}

In this updated code:

  • We import the `ProjectCard` component.
  • We define a `projects` array containing sample project data. Make sure to replace the placeholder image URLs with actual image paths.
  • We use the `map` function to render a `ProjectCard` for each project in the `projects` array.
  • The `grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6` classes create a responsive grid layout. On small screens, the cards will stack vertically. On medium and large screens, they will arrange into 2 and 3 columns, respectively.

Creating the Projects Page (projects.js)

Let’s create a dedicated page to showcase all your projects. Create a new file named `projects.js` inside the `pages` directory. This is a simplified approach using the older `pages` directory structure. For a more modern approach, create a `projects` directory inside the `app` directory and a `page.js` file within it.


// pages/projects.js
import ProjectCard from '../components/ProjectCard';

const projects = [
  // ... (same project data as in app/page.js)
];

const Projects = () => {
  return (
    <div>
      <section>
        <div>
          <h1>Projects</h1>
          <div>
            {projects.map((project, index) => (
              
            ))}
          </div>
        </div>
      </section>
    </div>
  );
};

export default Projects;

This page will display all your projects using the `ProjectCard` component, similar to the home page.

Creating the Contact Page (contact.js)

Now, let’s create a contact page where visitors can reach you. Create a file named `contact.js` inside the `pages` directory.


// pages/contact.js
const Contact = () => {
  return (
    <div>
      <section>
        <div>
          <h1>Contact Me</h1>
          <p>Feel free to reach out to me via email or social media.</p>
          <p>Email: <a href="mailto:[your_email]">[your_email]</a></p>
          {/* Add social media links here */}
        </div>
      </section>
    </div>
  );
};

export default Contact;

This code provides a basic contact page with your email address. You can add links to your social media profiles as well.

Adding Dynamic Project Pages

To create individual pages for each project, we can use dynamic routes in Next.js. Create a file named `[slug].js` inside the `pages` directory. This will allow us to create pages like `/project-1`, `/project-2`, etc.


// pages/[slug].js
import { useRouter } from 'next/router';
import Image from 'next/image';

const projects = [
  // ... (same project data as before)
];

const ProjectDetail = () => {
  const router = useRouter();
  const { slug } = router.query;

  const project = projects.find((p) => p.title.toLowerCase().replace(/ /g, '-') === slug);

  if (!project) {
    return <p>Project not found</p>;
  }

  return (
    <div>
      <section>
        <div>
          <h1>{project.title}</h1>
          
          <p>{project.description}</p>
          <a href="{project.link}" target="_blank" rel="noopener noreferrer">View Project</a>
        </div>
      </section>
    </div>
  );
};

export default ProjectDetail;

In this code:

  • We use the `useRouter` hook to access the route parameters.
  • We get the `slug` from the `router.query` object. This `slug` will correspond to the project title in the URL.
  • We find the corresponding project from the `projects` array based on the `slug`.
  • We display the project details, including the image, description, and a link to the project.

To make the links work, you’ll need to update the `ProjectCard.js` to link to the dynamic route:


// components/ProjectCard.js
import Link from 'next/link';
import Image from 'next/image';

const ProjectCard = ({ project }) => {
  const slug = project.title.toLowerCase().replace(/ /g, '-');

  return (
    <div>
      
      <div>
        <h3>{project.title}</h3>
        <p>{project.description}</p>
        
          <a>View Project</a>
        
      </div>
    </div>
  );
};

export default ProjectCard;

Now, when you click on a project card, it will navigate to a dynamic page with the project details.

Adding Images and Styling

To make your portfolio visually appealing, you’ll need to add images and further customize the styling. Here are some tips:

  • Image Optimization: Use the Next.js `Image` component for optimized image loading. This component automatically optimizes images for different screen sizes and formats.
  • Image Sizes: Provide appropriate `width` and `height` attributes to the `Image` component. This helps the browser reserve space for the image and prevents layout shifts.
  • Custom Styling: Use Tailwind CSS classes to customize the appearance of your website. You can modify colors, fonts, spacing, and other styles to match your brand.
  • Responsive Design: Ensure your website is responsive by using Tailwind CSS’s responsive prefixes (e.g., `md:`, `lg:`) to adjust the layout for different screen sizes.

Deployment

Once you’ve finished building your portfolio website, you’ll need to deploy it so that it’s accessible online. Here are a few deployment options:

  • Vercel: Vercel is the easiest way to deploy Next.js applications. It provides automatic deployments, CDN, and other features. Simply connect your GitHub repository to Vercel, and it will automatically deploy your website whenever you push changes.
  • Netlify: Netlify is another popular platform for deploying static sites and web applications. It offers similar features to Vercel.
  • Other Hosting Providers: You can also deploy your website to other hosting providers like AWS, Google Cloud, or DigitalOcean. However, this may require more manual configuration.

Follow the instructions provided by your chosen hosting provider to deploy your website.

Common Mistakes and How to Fix Them

  • Incorrect Tailwind CSS Setup: Ensure that you have correctly installed and configured Tailwind CSS. Double-check that you have included the Tailwind directives in your global CSS file and that the paths to your template files are correctly specified in your `tailwind.config.js` file.
  • Image Optimization Issues: Make sure you are using the Next.js `Image` component and providing the correct `width` and `height` attributes. Also, ensure that your images are optimized for the web.
  • Routing Errors: Double-check your file structure and the way you’re using the `Link` component for navigation. Make sure your dynamic routes are set up correctly.
  • CSS Specificity Conflicts: If you’re encountering styling issues, make sure there are no CSS specificity conflicts. You can use the browser’s developer tools to inspect the styles and identify any conflicts.
  • Deployment Issues: If you’re having trouble deploying your website, review the deployment instructions provided by your hosting provider. Make sure your project is correctly configured and that all dependencies are installed.

Key Takeaways

  • Next.js and Tailwind CSS are powerful tools for building modern and responsive portfolio websites.
  • Next.js provides excellent performance, SEO, and routing capabilities.
  • Tailwind CSS simplifies the styling process and promotes consistency.
  • Project structure and organization are important for maintainability.
  • Always optimize your images and test your website on different devices.

FAQ

  1. Can I use a different CSS framework instead of Tailwind CSS?

    Yes, you can use any CSS framework you prefer, such as Bootstrap, Material UI, or Styled Components. However, Tailwind CSS is particularly well-suited for Next.js due to its utility-first approach, which aligns well with Next.js’s component-based architecture.

  2. How do I add a contact form to my portfolio website?

    You can use a third-party service like Formspree or Netlify Forms, or you can build your own contact form using serverless functions. This involves creating a form in your HTML, handling the form submission with JavaScript, and sending the data to a serverless function that processes the data and sends an email.

  3. How do I optimize my website for SEO?

    Use descriptive page titles and meta descriptions. Use semantic HTML elements (e.g., `

    `, `

    `, `

    `). Optimize your images for size and use descriptive alt text. Submit your sitemap to search engines. Ensure your website is mobile-friendly and loads quickly.

  4. How can I add a blog to my portfolio website?

    You can create a blog section by creating a series of blog post components and displaying them on a dedicated blog page. You can use a content management system (CMS) like Contentful or Sanity to manage your blog content, or you can store your blog posts as Markdown files and use a library like `gray-matter` to parse them.

  5. How do I handle different screen sizes?

    Tailwind CSS provides responsive prefixes like `sm:`, `md:`, `lg:`, and `xl:` that allow you to apply different styles based on screen size. You can also use CSS media queries to customize your website’s layout for different devices.

Building a portfolio website with Next.js and Tailwind CSS is a rewarding project that can significantly improve your online presence. By following the steps in this tutorial, you’ve created a functional and visually appealing portfolio. Remember that the best portfolios are those that are constantly evolving. Continue to update your website with your latest projects, refine your design, and experiment with new features to keep your portfolio fresh and engaging. The skills you’ve gained in this tutorial are applicable to a wide range of web development projects, opening doors to new opportunities and allowing you to showcase your abilities to the world.