Building Portfolio in Next.js
Tutorials
Building Portfolio in Next.js

By Subash Maharjan


Chapters
  • Chapter 1

    Introduction

  • Chapter 2

    Project Architecture

  • Chapter 3

    SCSS Design Tokens

  • Chapter 4

    The Hero Component

  • Chapter 5

    The MDX Content Layer

  • Chapter 6

    Syntax Highlighting & Project Filters

  • Chapter 7

    Smooth Layout Transitions

  • Chapter 8

    Modern Contact Forms

  • Chapter 9

    SEO & Deployment

  • Chapter 10

    Lighthouse Performance & Blogging

  • Chapter 11

    Interactive Playgrounds & Subscriptions

  • Chapter 12

    Dynamic Social Previews & Launch

  • Chapter 13

    Project Summary for Recruiters

  • building portfolio in nextjs

    Smooth Layout Transitions

    Chapter 7

    2 min read

    On this page
    1. The Animated Grid (`ProjectGrid.tsx`)2. Styling the Grid (ProjectGrid.module.scss)3. Creating the Project Card (ProjectCard.tsx)
    Company LogoSubash
    HOMEABOUTPORTFOLIOCONTENTSCONTACT

    To make your portfolio feel premium, we’ll use Framer Motion to animate the project cards as they filter. The magic happens with the layout prop, which automatically calculates the delta between positions.

    1. The Animated Grid (ProjectGrid.tsx)

    Since animations require browser APIs, this must be a Client Component. We use AnimatePresence to handle cards being added or removed from the DOM.

    "use client";
    import { motion, AnimatePresence } from 'framer-motion';
    import ProjectCard from './ProjectCard';
    import styles from './ProjectGrid.module.scss';
     
    export default function ProjectGrid({ projects }: { projects: any[] }) {
      return (
        <motion.div layout className={styles.grid}>
          <AnimatePresence mode='popLayout'>
            {projects.map((project) => (
              <motion.div
                key={project.slug}
                layout
                initial={{ opacity: 0, scale: 0.9 }}
                animate={{ opacity: 1, scale: 1 }}
                exit={{ opacity: 0, scale: 0.9 }}
                transition={{ duration: 0.3 }}
              >
                <ProjectCard {...project} />
              </motion.div>
            ))}
          </AnimatePresence>
        </motion.div>
      );
    }

    2. Styling the Grid (ProjectGrid.module.scss)

    Use your global mixins to ensure the grid is responsive.

    .grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: $spacing-md;
      margin-top: $spacing-lg;
     
      @include tablet {
        grid-template-columns: repeat(2, 1fr);
      }
     
      @include mobile {
        grid-template-columns: 1fr;
        gap: $spacing-sm;
      }
    }

    3. Creating the Project Card (ProjectCard.tsx)

    A standard card utilizing Next.js Image for performance.

    import Image from 'next/image';
    import Link from 'next/link';
    import styles from './ProjectCard.module.scss';
     
    export default function ProjectCard({ title, slug, image, tags }: any) {
      return (
        <Link href={`/portfolio/${slug}`} className={styles.card}>
          <div className={styles.imageWrapper}>
            <Image 
              src={image} 
              alt={title} 
              fill 
              sizes="(max-width: 768px) 100vw, 33vw"
            />
          </div>
          <div className={styles.content}>
            <h3>{title}</h3>
            <div className={styles.tags}>
              {tags.map((tag: string) => (
                <span key={tag}>{tag}</span>
              ))}
            </div>
          </div>
        </Link>
      );
    }

    Why use popLayout?

    In AnimatePresence, mode='popLayout' allows exiting elements to be "popped" out of the flex/grid flow immediately. This prevents the remaining cards from "jumping" to their new positions instantly, allowing for a smooth sliding transition.

    HOME
    ABOUT
    PORTFOLIO
    CONTENTS
    CONTACT

    © 2026Subash Maharjan™

    Made byHudeoworks Design