Steps to build a portfolio website using Next.js and Tailwind with Dark Mode Support.
You can build a portfolio like mine after reading this article.
A portfolio is a place where you can showcase all your skills to the world. As a developer, you really need a portfolio to showcase your projects, blogs, and much more.
But why do we need to build a portfolio with Next.js? We all know next.js is react framework with out-of-box performance. So you are not required to worry about your portfolio performance. Everything is taken care of by next.js and Vercel. Let's get into action
Next.js and Tailwind Configuration
We can configure tailwind with next.js with a single command. As below:
npx create-next-app -e with-tailwindcss your-portfolio-name
The above command automatically configures your Tailwind setup based on the official Next.js example.
Once the installation is completed navigate to your project folder using cd your-portfolio-name
and start the dev server using yarn dev
command. You can see the below page if you hit http://localhost:3000
in the browser.
Creating Navigation Section
Create a components
folder in the root directory to add your portfolio components. Create a file called Navigation.js
and add the below code:
import Link from "next/link" | |
import React from "react" | |
const Navigation = () => { | |
return ( | |
<div className="sticky top-0 z-20 py-2 bg-white md:py-6 md:mb-6 dark:bg-black"> | |
<div className="container px-4 mx-auto lg:max-w-4xl flex items-center justify-between"> | |
<Link href="/"> | |
<a | |
className={"font-medium tracking-wider transition-colors text-gray-900 hover:text-sky-500 uppercase dark:text-white"} | |
> | |
Your Name | |
</a> | |
</Link> | |
</div> | |
</div> | |
) | |
} | |
export default Navigation; |
The navigation component is the header section of your portfolio. In the above code, you can see dark:
class to support dark mode. Add the Navigation
component to _app.js
file as below. MyApp component is used to initialize pages.
import 'tailwindcss/tailwind.css' | |
import Navigation from "../components/Navigation"; | |
function MyApp({ Component, pageProps }) { | |
return ( | |
<> | |
<Navigation/> | |
<Component {...pageProps} /> | |
</> | |
) | |
} | |
export default MyApp |
Creating Footer Section
To add a Footer with social links, Create a Footer.js file in the components folder and add the below code:
import React from "react"; | |
const Footer = () => { | |
return ( | |
<div className="mt-12 lg:mt-18 sm:pb-36 sm:py-12 py-6"> | |
<div className="max-w-4xl px-4 mx-auto text-gray-800 dark:text-white"> | |
<div className="pb-8 mb-2 border-t-2 border-gray-300 dark:border-white-300"></div> | |
<div className="flex flex-col justify-between lg:flex-row items-center"> | |
<p>Built with Next.js, Tailwind and Vercel</p> | |
<div className="flex flex-wrap pt-2 sm:space-x-4 space-x-2 font-medium lg:pt-0"> | |
<a | |
href="#" | |
className={"transition-colors hover:text-yellow-500"} | |
target="_blank" | |
rel="noreferrer" | |
> | |
</a> | |
<a | |
href="#" | |
className={"transition-colors hover:text-yellow-500"} | |
target="_blank" | |
rel="noreferrer" | |
> | |
</a> | |
<a | |
href="#" | |
className={"transition-colors hover:text-yellow-500"} | |
target="_blank" | |
rel="noreferrer" | |
> | |
GitHub | |
</a> | |
<a | |
href="#" | |
className={"transition-colors hover:text-yellow-500"} | |
target="_blank" | |
rel="noreferrer" | |
> | |
Medium | |
</a> | |
<a | |
href="#" | |
className={"transition-colors hover:text-yellow-500"} | |
target="_blank" | |
rel="noreferrer" | |
> | |
DEV | |
</a> | |
<a | |
href="#" | |
className={"transition-colors hover:text-yellow-500"} | |
target="_blank" | |
rel="noreferrer" | |
> | |
Hashnode | |
</a> | |
</div> | |
</div> | |
</div> | |
</div> | |
) | |
} | |
export default Footer; |
Add the Footer.js
component to _app.js
file as below
import 'tailwindcss/tailwind.css' | |
import Navigation from "../components/Navigation"; | |
import Footer from "../components/Footer"; | |
function MyApp({Component, pageProps}) { | |
return <> | |
<Navigation/> | |
<Component {...pageProps} /> | |
<Footer/> | |
</> | |
} | |
export default MyApp |
About Component
Create About.js
file inside components folder and add the below code:
import React from "react" | |
import Image from "next/image" | |
import profile from "../public/profile.jpeg" | |
const About = () => { | |
return ( | |
<div className="container px-4 mx-auto"> | |
<div className="lg:space-x-5 lg:flex lg:flex-row item-center lg:-mx-4 flex flex-col-reverse text-center lg:text-left"> | |
<div className="lg:px-4 lg:mt-12 "> | |
<h1 className="text-2xl font-bold text-gray-900 lg:text-5xl dark:text-white"> | |
Hey there, | |
</h1> | |
<div className="mt-6 text-gray-800 dark:text-white"> | |
<p className="mb-4"> | |
Lorem Ipsum is simply dummy text of the printing and typesetting industry. | |
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, | |
when an unknown printer took a galley of type and scrambled it to make a | |
type specimen book. | |
</p> | |
</div> | |
</div> | |
<div className="flex-shrink-0 lg:mt-12 lg:px-4 mb-10"> | |
<Image | |
src={profile} | |
alt="Profile" | |
priority={true} | |
className="rounded-full" | |
width={250} | |
height={250} | |
placeholder="blur" | |
/> | |
</div> | |
</div> | |
</div> | |
) | |
} | |
export default About; |
I just added some dummy texts above for the demo. Include your profile image from the public folder as above. Placeholder blur
prop in image component is to add loading effects. Import About
component to index.js
file as below:
import Head from 'next/head' | |
import About from "../components/About"; | |
export default function Home() { | |
return ( | |
<div className="space-y-14 lg:space-y-24"> | |
<Head> | |
<title>Create Next App</title> | |
<link rel="icon" href="/favicon.ico" /> | |
</Head> | |
<main className="max-w-4xl mx-auto mt-16 antialiased"> | |
<About/> | |
</main> | |
</div> | |
) | |
} |
I have removed the older template code and added the above code. Now your portfolio looks like below:
Dark Mode Support
Now let's add dark mode to our portfolio. Adding dark mode is very simple. Create ThemeSwitch
component to add a toggle switch to toggle between Dark Mode and Light Mode.
To support Dark Mode in Next.js we need to add next-themes
package to our dependency. Now import useTheme
from next-theme to ThemeSwitch Component as below:
import { useEffect, useState } from 'react' | |
import { useTheme } from 'next-themes' | |
const ThemeSwitch = () => { | |
const [mounted, setMounted] = useState(false) | |
const { theme, setTheme, resolvedTheme } = useTheme() | |
// When mounted on client, now we can show the UI | |
useEffect(() => setMounted(true), []) | |
return ( | |
<button | |
aria-label="Toggle Dark Mode" | |
type="button" | |
className="w-8 h-8 p-1 ml-1 mr-1 rounded sm:ml-4" | |
onClick={() => setTheme(theme === 'dark' || resolvedTheme === 'dark' ? 'light' : 'dark')} | |
> | |
<svg | |
xmlns="http://www.w3.org/2000/svg" | |
viewBox="0 0 20 20" | |
fill="currentColor" | |
className="text-gray-900 dark:text-gray-100" | |
> | |
{mounted && (theme === 'dark' || resolvedTheme === 'dark') ? ( | |
<path | |
fillRule="evenodd" | |
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" | |
clipRule="evenodd" | |
/> | |
) : ( | |
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" /> | |
)} | |
</svg> | |
</button> | |
) | |
} | |
export default ThemeSwitch |
Add ThemeSwitch
Component to Navigation
component and include next-themes
ThemeProvider in _app.js
as below:
import 'tailwindcss/tailwind.css' | |
import Navigation from "../components/Navigation"; | |
import { ThemeProvider } from 'next-themes' | |
function MyApp({ Component, pageProps }) { | |
return <> | |
<ThemeProvider attribute="class" enableSystem={false}> | |
<Navigation/> | |
<Component {...pageProps} /> | |
</ThemeProvider> | |
</> | |
} | |
export default MyApp |
attribute=class
is to enable dark and light mode manually. I have disabled system preference by enableSystem=false
Change darkMode
option to class
in tailwind.config.js
file to toggle dark mode manually instead of relying on the operating system preference.
module.exports = { | |
mode: 'jit', | |
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], | |
darkMode: 'class', // or 'media' or 'class' | |
theme: { | |
extend: {}, | |
}, | |
variants: { | |
extend: {}, | |
}, | |
plugins: [], | |
} |
After the above changes, our portfolio looks like below:
Light Mode
Dark Mode
Deploying in Vercel
You can deploy your portfolio in Vercel within 2 steps as below:
- Create a Vercel Account
- Connect your repository and click deploy.
Conclusion
You can add multiple pages like projects, blogs by creating new files inside the pages folder. I hope you have found this useful.
Thank you for reading.
Get more updates on Twitter.
You can support me by buying me a coffee ☕
eBook
Debugging ReactJS Issues with ChatGPT: 50 Essential Tips and Examples
ReactJS Optimization Techniques and Development Resources
Twitter Realtime Followers Count
More Blogs
- Twitter Followers Tracker using Next.js, NextAuth and TailwindCSS
- 10 React Packages with 1K UI Components
- No More ../../../ Import in React
- Redux Toolkit - The Standard Way to Write Redux
- 5 Packages to Optimize and Speed Up Your React App During Development
- How To Use Axios in an Optimized and Scalable Way With React
- 15 Custom Hooks to Make your React Component Lightweight
- 10 Ways to Host Your React App For Free
- How to Secure JWT in a Single-Page Application
- Redux Auth Starter: A Zero Config CRA Template
Top comments (3)
Looking good!
I built my personal website with this exact setup a few months ago:
thomasledoux.be
Amazing work!
Can you extend this tutorial to create the portfolio exactly like yours ?
Some comments have been hidden by the post's author - find out more