Building my portfolio website using Next js, Framer Motion, TypeScript, Tailwind CSS
This article is about the code used to build my portfolio project.
1. Setup
npx create-next-app@latest
npx create-next-app@latest .
to install in the current directory.
to run server
npm run dev
To remove the favicon.ico:
stop the server using ctrl + c and type 'y' in the vscode terminal.
delete the .next folder
now start the server using the cmd
npm run dev
, the .next folder will be created automatically.
to check whether you have tailwind css installed or not: npm view tailwindcss version
absolute will take the div out of its flow.
CSS code for the bg blur:
<div className="bg-[#fbe2e3] absolute top-[-6rem] -z-10 right-[11rem] h-[31.25rem] w-[31.25rem] rounded-full blur-[10rem] sm:w-[68.75rem] dark:bg-[#946263]"></div>
<div className="bg-[#dbd7fb] absolute top-[-1rem] -z-10 left-[-35rem] h-[31.25rem] w-[50rem] rounded-full blur-[10rem] sm:w-[68.75rem] md:left-[-33rem] lg:left-[-28rem] xl:left-[-15rem] 2xl:left-[-5rem] dark:bg-[#676394]"></div>
create a lib folder ๐.
create a data.ts file in the lib folder ๐.
paste the entire code in the data.ts file:
import React from "react";
import { CgWorkAlt } from "react-icons/cg";
import { FaReact } from "react-icons/fa";
import { LuGraduationCap } from "react-icons/lu";
import corpcommentImg from "@/public/corpcomment.png";
import rmtdevImg from "@/public/rmtdev.png";
import wordanalyticsImg from "@/public/wordanalytics.png";
export const links = [
{
name: "Home",
hash: "#home",
},
{
name: "About",
hash: "#about",
},
{
name: "Projects",
hash: "#projects",
},
{
name: "Skills",
hash: "#skills",
},
{
name: "Experience",
hash: "#experience",
},
{
name: "Contact",
hash: "#contact",
},
] as const;
export const experiencesData = [
{
title: "Graduated bootcamp",
location: "Miami, FL",
description:
"I graduated after 6 months of studying. I immediately found a job as a front-end developer.",
icon: React.createElement(LuGraduationCap),
date: "2019",
},
{
title: "Front-End Developer",
location: "Orlando, FL",
description:
"I worked as a front-end developer for 2 years in 1 job and 1 year in another job. I also upskilled to the full stack.",
icon: React.createElement(CgWorkAlt),
date: "2019 - 2021",
},
{
title: "Full-Stack Developer",
location: "Houston, TX",
description:
"I'm now a full-stack developer working as a freelancer. My stack includes React, Next.js, TypeScript, Tailwind, Prisma and MongoDB. I'm open to full-time opportunities.",
icon: React.createElement(FaReact),
date: "2021 - present",
},
] as const;
export const projectsData = [
{
title: "CorpComment",
description:
"I worked as a full-stack developer on this startup project for 2 years. Users can give public feedback to companies.",
tags: ["React", "Next.js", "MongoDB", "Tailwind", "Prisma"],
imageUrl: corpcommentImg,
},
{
title: "rmtDev",
description:
"Job board for remote developer jobs. I was the front-end developer. It has features like filtering, sorting and pagination.",
tags: ["React", "TypeScript", "Next.js", "Tailwind", "Redux"],
imageUrl: rmtdevImg,
},
{
title: "Word Analytics",
description:
"A public web app for quick analytics on text. It shows word count, character count and social media post limits.",
tags: ["React", "Next.js", "SQL", "Tailwind", "Framer"],
imageUrl: wordanalyticsImg,
},
] as const;
export const skillsData = [
"HTML",
"CSS",
"JavaScript",
"TypeScript",
"React",
"Next.js",
"Node.js",
"Git",
"Tailwind",
"Prisma",
"MongoDB",
"Redux",
"GraphQL",
"Apollo",
"Express",
"PostgreSQL",
"Python",
"Django",
"Framer Motion",
] as const;
To use an SVG file in your React project, you can import it and then use it as a component. Here's how you can do it:
import using @@
,
"paths": {
"@/*": ["./src/*"],
"@@/*": ["./*"]
}
then use the varibale.
icon: GraduateCap,
2. Header
create components folder ๐
create header.tsx file.
install es7 + extension for boilerplates.
use rfc
install framer motion:
npm install framer-motion
"use client"; to tell header.tsx file is a client component.
<header className="z-[999] relative">
<motion.div
className="fixed top-0 left-1/2 h-[4.5rem] w-full rounded-none border border-white border-opacity-40 bg-white bg-opacity-80 shadow-lg shadow-black/[0.03] backdrop-blur-[0.5rem] sm:top-6 sm:h-[3.25rem] sm:w-[36rem] sm:rounded-full dark:bg-gray-950 dark:border-black/40 dark:bg-opacity-75"
initial={{ y: -100, x: "-50%", opacity: 0 }}
animate={{ y: 0, x: "-50%", opacity: 1 }}
></motion.div>
<nav className="flex fixed top-[0.15rem] left-1/2 h-12 -translate-x-1/2 py-2 sm:top-[1.7rem] sm:h-[initial] sm:py-0">
<ul className="flex w-[22rem] flex-wrap items-center justify-center gap-y-1 text-[0.9rem] font-medium text-gray-500 sm:w-[initial] sm:flex-nowrap sm:gap-5">
{links.map((link) => (
<motion.li
className="h-3/4 flex items-center justify-center relative"
key={link.hash}
initial={{ y: -100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
>
<Link
className={
"flex w-full items-center justify-center px-3 py-3 hover:text-gray-950 transition dark:text-gray-500 dark:hover:text-gray-300"
}
href={link.hash}
>
{link.name}
</Link>
</motion.li>
))}
</ul>
</nav>
</header>
3. Intro section
create intro.tsx file in components folder ๐
Everything belongs to same semantic meaning than using section.
In the page.tsx:
add the intro and place the cursor and hit: ctrl + spacebar and select the intro.tsx file to import into page.tsx.
modify next config mjs file to include the external image.
Extend eslintrc json to remove the apos syntax errors rules
{
"extends": "next/core-web-vitals",
"rules": {
"react/no-unescaped-entities": 0
}
}
to use svg files
i have used image tag.
install
npm install @react-spring/web
4. Section divider
create section-divider file.
"use client";
import React from "react";
export default function SectionDivider() {
return (
<div
className="bg-gray-200 my-24 h-16 w-1 rounded-full hidden sm:block dark:bg-opacity-20"
></div>
);
}
add to page.tsx
Using rough notation library
npm install --save react-rough-notation
5. About section
create about.tsx file
use rfc
create a file section-heading.tsx
6. Projects section
create projects.tsx file
create project.tsx file
7. Finish header
using scroll property to scroll to the sections.
adding smooth scroll for all the layout.
keeping track of active section using state.
install clsx framework to apply classes conditionally
npm install --save clsx
conditionally applying background color
{
link.name === activeSection && (
<motion.span
className="bg-gray-100 rounded-full absolute inset-0 -z-10 dark:bg-gray-800"
layoutId="activeSection"
transition={{
type: "spring",
stiffness: 380,
damping: 30,
}}
></motion.span>
)
}
using onclick:
onClick={() => {
setActiveSection(link.name);
setTimeOfLastClick(Date.now());
}}
as you scroll the active session should change the background not only when you click:
intersection observer API in react intersection observer
npm install react-intersection-observer --save
create context folder and create active-section-context.tsx file
create hook.ts file in lib folder ๐
create types.ts file in lib folder ๐
8. Skills section
create a skills.tsx file
9. Experience section
create experience.tsx file
npm i react-vertical-timeline-component
types declaration package
npm i --save-dev @types/react-vertical-timeline-component
create theme-context.tsx file
Error:
Server Error Error: Cannot read properties of undefined (reading 'prototype')
"use client";
10. Contact section
create contact.tsx file
toast
npm install react-hot-toast
create email folder and contact-form-email.tsx file
npm i @react-email/components
create actions folder ๐ and sendEmail.ts file:
create utils.ts file in lib folder
create submit-btn.tsx file in component folder
improved:
11. Footer
create footer.tsx file
added functionality to let user like
adding error checks
Final:
Conclusion
Learning Objectives,
Setup using next js,
Project setup.
Installing dependencies,
using scroll property to scroll to the sections.
implemented toast libs.
Implemented predefined text messages.
Added functionality to like
Added error checks