Social media management plays a very major role in the success of businesses, influencers, and organizations alike. With the rapidly growing number of platforms and the need for real-time engagement, having a centralized hub to monitor and manage social media activities is important. In this article, you will learn to build a Social Media Dashboard With Next.js.
Project Preview:

Prerequisites:
Approach
- Setting up Next.js Environment: Install Node.js and set up a Next.js project using create-next-app.
- Implement Authentication: Integrate authentication using NextAuth.js and OAuth for social media platforms.(Optional for now).
- Connect Social Media APIs: Utilize APIs of social media platforms for fetching data, posting content, and managing accounts.(Further Enhancement work)
- Develop Dashboard Features: Building components for overview of the growth from different social media platform comparing all together in a single place.
Steps to Create Social Media Dashboard With Next.js
Step 1. Set up Next.js project using the command:
npx create-next-app social-media-dashboard The create-next-app command will sets up everything automatically for you, and on the terminal you will see this,
What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias (@/*)? No / Yes
What import alias would you like configured? @/*
Just go for yes on every option otherwise keep it default and hit enter.
Step 2. Navigate to the project folder using:
cd social-media-dashboard Project Structure

You can put any logo you need in the public\assets directoryDependencies
After following the installation steps the dependencies in package.json file will be somewhat like this:
"dependencies": {
"@next/font": "13.1.6",
"eslint": "8.33.0",
"eslint-config-next": "13.1.6",
"next": "13.1.6",
"react": "18.2.0",
"react-dom": "18.2.0",
"standard": "^17.0.0"
}
Example: Here is the example code to create a dashboard using Next.js.
// src\components\Card.jsx
import Image from 'next/image'
import React, { useEffect, useState } from 'react'
import CountFollowers from './CountFollowers'
const Card = ({ icon, mediaName, userName,
followers, type, isUp, recentNotify, arrow }) => {
return (
<div className='relative flex flex-col items-center
dark:bg-card-light dark:text-text-light
dark:hover:bg-[#dadeeb] hover:bg-[#45455c]
bg-card-dark text-text-dark h-auto rounded
overflow-hidden transition-colors ease-linear
duration-300 w-full cursor-pointer '>
<span className={`${mediaName === 'facebook' ?
'bg-fb-color' : ''} ${mediaName === 'twitter' ?
'bg-tw-color' : ''} ${mediaName === 'instagram' ?
'bg-gradient-to-r from-ig-left-gradient to-ig-right-gradient'
: ''} ${mediaName === 'youtube' ? 'bg-yt-color' : ''}
h-[4px] w-full block absolute top-0`} />
<div className='flex mt-8 mb-7'>
<Image src={icon} height={20} width={20}
alt={`icon_${mediaName}`} />
<p className='ml-2 font-semibold'>@{userName}</p>
</div>
<div className='text-center'>
<CountFollowers followers={followers} card={true} />
<p className='uppercase space tracking-[.25em] mt-2'>{type}</p>
</div>
{/* <span className='mb-6 mt-7' > */}
<p className={`${isUp ? 'text-text-green-arrow'
: 'text-text-red-arrow'} mb-6 mt-7`}>
<Image src={arrow} width={12} height={15}
className='inline-block' alt='arrow_stat' />
{recentNotify}Today</p>
{/* </span> */}
</div>
)
}
export default Card
// src\components\CountFollowers.jsx
import React, { useEffect, useLayoutEffect, useState } from 'react'
const CountFollowers = ({ followers, card }) => {
const [counter, setCounter] = useState(0)
let animInterval;
function startAnim() {
animInterval = setInterval(animNumValue, 15, counter, followers);
}
function animNumValue(num, newValue) {
let valor = num
let diferencia = newValue - valor;
if (counter >= followers) return
if (diferencia > 1500) {
valor += 519;
} else if (diferencia > 400) {
valor += 200;
} else if (diferencia > 200) {
valor += 100;
} else if (diferencia > 100) {
valor += 20;
}
else {
valor++;
}
setCounter(valor)
}
useEffect(() => {
if (!(counter >= followers)) startAnim()
return (() => {
clearInterval(animInterval)
})
}, [counter])
return (
<p className={`${card ? 'text-[4rem] tracking-[3px]'
: 'text-4xl md:text-4xl '} font-bold dark:text-titles-light
dark:group-hover:text-white text-titles-dark `}>
{`${counter.toString().replace('000', "k")}`}</p>
)
}
export default CountFollowers
// src\components\DetailsCard.jsx
import Image from "next/image";
import React from "react";
import CountFollowers from "./CountFollowers";
const DetailsCard = ({
mediaName,
icon,
arrow,
isUp,
sectionName,
state,
percentage,
}) => {
return (
<div className="flex flex-col items-center justify-center
dark:bg-card-light dark:text-text-light bg-card-dark text-text-dark
dark:hover:bg-[#dadeeb] hover:bg-[#45455c] cursor-pointer h-auto
rounded overflow-hidden transition-colors ease-linear duration-300
gap-10 w-full px-8 py-6">
<div className="flex justify-between w-full items-center">
<p className="font-semibold">{sectionName}</p>
<Image src={icon} width={22} height={22}
alt={`${mediaName}_icon`} />
</div>
<div className="flex justify-between w-full items-center">
<CountFollowers followers={state} card={false} />
<div className={`${isUp ?
"text-text-green-arrow" : "text-text-red-arrow"}`}>
<Image src={arrow} width={12} height={15}
className="inline-block" alt="arrow-stat" />
{percentage}
</div>
</div>
</div>
);
};
export default DetailsCard;
// src\components\Header.jsx
import React from 'react'
import Layout from './Layout'
import ToogleTheme from './ToogleTheme'
const Header = () => {
return (
<header className='dark:bg-header-light dark:text-text-light
bg-header-dark text-text-dark w-full h-64 md:h-[280px]
rounded-b-3xl pt-10'>
<Layout>
<div className='flex flex-col md:flex-row
justify-between md:items-center'>
<div>
<h1 className='dark:text-titles-light text-titles-dark
text-2xl md:text-4xl mb-1 font-bold'>
Social Media Dashboard</h1>
<p className='font-bold'> Total Followers: 111,101</p>
</div>
<hr className='dark:bg-slate-600 bg-slate-200
h-[2px] md:hidden my-5' />
<ToogleTheme />
</div>
</Layout>
</header>
)
}
export default Header
// src\components\Layout.jsx
import React from 'react'
const Layout = ({ children }) => {
return (
<div className='w-[85%] m-auto
max-w-[1440px]'>{children}</div>
)
}
export default Layout
// src\components\ToogleTheme.jsx
import Head from 'next/head'
import React, { useEffect, useState } from 'react'
const ToogleTheme = () => {
const [darkTheme, setDarkTheme] = useState(false)
useEffect(() => {
if (localStorage.getItem("theme") === 'true') {
setDarkTheme(true)
document.documentElement.classList.add('dark')
} else {
setDarkTheme(false)
document.documentElement.classList.remove('dark')
}
}, [])
const handleTheme = () => {
if (darkTheme) {
setDarkTheme(false)
document.documentElement.classList.remove('dark')
localStorage.setItem('theme', false)
} else {
setDarkTheme(true)
localStorage.setItem('theme', true)
document.documentElement.classList.add('dark')
}
}
return (
<>
<Head>
<meta name="theme-color" content={darkTheme ?
'#F8F8FA' : '#171723'} />
</Head>
<div className='flex justify-between
md:justify-start items-center md:gap-3'>
<h2 className='font-bold'>Dark Mode</h2>
<div className={`w-16 h-8 rounded-full
${darkTheme ? 'bg-toggle-light hover:bg-gradient-to-r
from-toogle-gradient-left to-toogle-gradient-right ' : '
bg-gradient-to-r from-toogle-gradient-left to-toogle-gradient-right'}
transition-all ease-in-out duration-500 flex flex-col justify-center`}>
<span onClick={handleTheme} className={`w-6 h-6 cursor-pointer
rounded-full dark:bg-white bg-toggle-dark transition-all
ease-in-out duration-500
${darkTheme ? 'ml-9' : 'ml-1'}`}></span>
</div>
</div >
</>
)
}
export default ToogleTheme
// src\pages\_app.js
import '@/styles/globals.css'
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />
}
// src\pages\_document.js
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang="en">
<Head />
<body className='dark:bg-light bg-dark
dark:text-light-dark text-titles-dark min-h-screen'>
<Main />
<NextScript />
</body>
</Html>
)
}
// src\pages\index.js
import Head from "next/head";
import { Inter } from "@next/font/google";
import Header from "@/components/Header";
import Layout from "@/components/Layout";
import Card from "@/components/Card";
import DetailsCard from "@/components/DetailsCard";
const inter = Inter({
subsets: ["latin"],
weight: ["400", "700"],
preload: true,
});
export default function Home({ cards, details }) {
return (
<>
<Head>
<title>Social Media Dashboard</title>
<meta
name="description"
content="Social Media Dashboard Design with Next.js"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={`${inter.className} m-auto bg-pink-100`}>
<Header />
<Layout>
<div className="grid xl:grid-cols-2 place-content-between
gap-8 sm:grid-cols-2 -mt-10 md:-mt-[120px]
ml-auto mr-auto text-center">
{cards.map(renderCard)}
</div>
<h2 className="dark:text-text-light text-titles-dark
font-bold text-2xl my-6 md:mt-10">
Overview - Today
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2
xl:grid-cols-4 md:flex-row md:justify-between
items-center flex-wrap gap-8 mb-12">
{details.map(renderDetailsCard)}
</div>
</Layout>
</main>
</>
);
}
function renderCard(card, index) {
return (
<Card
key={card.mediaName + index}
icon={card.icon}
mediaName={card.mediaName}
isUp={card.isUp}
userName={card.username}
type={card.type}
followers={card.followers}
recentNotify={card.recentNotify}
arrow={card.arrow}
/>
);
}
function renderDetailsCard(detail, index) {
return (
<DetailsCard
key={detail.mediaName + index}
mediaName={detail.mediaName}
icon={detail.icon}
arrow={detail.arrow}
isUp={detail.isUp}
sectionName={detail.sectionName}
state={detail.state}
percentage={detail.percentage}
/>
);
}
export async function getStaticProps() {
const cards = [
{
icon: "./../assets/facebook-svgrepo-com.svg",
mediaName: "facebook",
username: "Geek",
type: "followers",
followers: 1234,
recentNotify: 12,
arrow: "./../assets/icon-up.svg",
isUp: true,
},
{
icon: "./../assets/twitter-svgrepo-com.svg",
mediaName: "twitter",
username: "Geek",
type: "followers",
followers: 8294,
recentNotify: 99,
arrow: "./../assets/icon-up.svg",
isUp: true,
},
{
icon: "./../assets/instagram-svgrepo-com.svg",
mediaName: "instagram",
username: "realGeek",
type: "followers",
followers: 86000,
recentNotify: 1099,
arrow: "./../assets/icon-up.svg",
isUp: true,
},
{
icon: "./../assets/youtube-svgrepo-com.svg",
mediaName: "youtube",
username: "Geek.",
type: "subscribers",
followers: 5432,
recentNotify: 144,
arrow: "./../assets/icon-down.svg",
isUp: false,
},
// Add more card objects here if needed
];
const details = [
{
mediaName: "facebook-views",
icon: "./../assets/facebook-svgrepo-com.svg",
arrow: "./../assets/icon-up.svg",
isUp: true,
sectionName: "Page Views",
state: 202,
percentage: "3%",
},
{
mediaName: "facebook-likes",
icon: "./../assets/facebook-svgrepo-com.svg",
arrow: "./../assets/icon-down.svg",
isUp: false,
sectionName: "Likes",
state: 40,
percentage: "2%",
},
{
mediaName: "instagram-likes",
icon: "./../assets/instagram-svgrepo-com.svg",
arrow: "./../assets/icon-up.svg",
isUp: true,
sectionName: "Likes",
state: 50101,
percentage: "2257%",
},
{
mediaName: "instagram-views",
icon: "./../assets/instagram-svgrepo-com.svg",
arrow: "./../assets/icon-up.svg",
isUp: true,
sectionName: "Profile Views",
state: 202000,
percentage: "1375%",
},
{
mediaName: "retweets",
icon: "./../assets/twitter-svgrepo-com.svg",
arrow: "./../assets/icon-up.svg",
isUp: true,
sectionName: "Retweets",
state: 513,
percentage: "303%",
},
{
mediaName: "twitter-likes",
icon: "./../assets/twitter-svgrepo-com.svg",
arrow: "./../assets/icon-up.svg",
isUp: true,
sectionName: "Likes",
state: 1013,
percentage: "553%",
},
{
mediaName: "youtube-likes",
icon: "./../assets/youtube-svgrepo-com.svg",
arrow: "./../assets/icon-down.svg",
isUp: false,
sectionName: "Likes",
state: 214,
percentage: "19%",
},
{
mediaName: "youtube-views",
icon: "./../assets/youtube-svgrepo-com.svg",
arrow: "./../assets/icon-down.svg",
isUp: false,
sectionName: "Total Views",
state: 3658,
percentage: "12%",
},
// Add more details objects here if needed
];
return {
props: {
cards,
details,
},
};
}
<!-- public\assets\facebook-svgrepo-com.svg -->
<svg xmlns="https://www.w3.org/2000/svg" x="0px" y="0px"
width="100" height="100" viewBox="0 0 40 40">
<path fill="#8bb7f0" d="M2.5 2.5H37.5V37.5H2.5z">
</path><path fill="#4e7ab5" d="M37,3v34H3V3H37 M38,2H2v36h36V2L38,2z">
</path><path fill="#fff" d="M27,37V24h4.93l0.698-5H27v-3.384c0-1.568,0.702-2.636,2.95-2.636L33,12.979V8.225 c-0.496-0.066-2.381-0.213-4.361-0.213c-4.134,0-6.639,2.523-6.639,7.157V19h-5v5h5v13H27z"></path>
</svg>
<!-- public\assets\icon-down.svg -->
<svg xmlns="https://www.w3.org/2000/svg" width="8" height="4">
<path fill="#DC414C" fill-rule="evenodd" d="M0 0l4 4 4-4z"/></svg>
<!-- public\assets\icon-up.svg -->
<svg xmlns="https://www.w3.org/2000/svg" width="8" height="4">
<path fill="#1EB589" fill-rule="evenodd" d="M0 4l4-4 4 4z"/></svg>
<!-- public\assets\instagram-svgrepo-com.svg -->
<svg xmlns="https://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100"
viewBox="0 0 48 48">
<radialGradient id="yOrnnhliCrdS2gy~4tD8ma_Xy10Jcu1L2Su_gr1" cx="19.38"
cy="42.035" r="44.899" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#fd5"></stop>
<stop offset=".328" stop-color="#ff543f">
</stop><stop offset=".348" stop-color="#fc5245"></stop>
<stop offset=".504" stop-color="#e64771"></stop>
<stop offset=".643" stop-color="#d53e91"></stop>
<stop offset=".761" stop-color="#cc39a4"></stop>
<stop offset=".841" stop-color="#c837ab"></stop>
</radialGradient><path fill="url(#yOrnnhliCrdS2gy~4tD8ma_Xy10Jcu1L2Su_gr1)"
d="M34.017,41.99l-20,0.019c-4.4,0.004-8.003-3.592-8.008-7.992l-0.019-20
c-0.004-4.4,3.592-8.003,7.992-8.008l20-0.019c4.4-0.004,8.003,3.592,8.008,7.992l0.019,20 C42.014,38.383,38.417,41.986,34.017,41.99z"></path>
<radialGradient id="yOrnnhliCrdS2gy~4tD8mb_Xy10Jcu1L2Su_gr2" cx="11.786"
cy="5.54" r="29.813" gradientTransform="matrix(1 0 0 .6663 0 1.849)"
gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#4168c9">
</stop><stop offset=".999" stop-color="#4168c9" stop-opacity="0"></stop>
</radialGradient><path fill="url(#yOrnnhliCrdS2gy~4tD8mb_Xy10Jcu1L2Su_gr2)"
d="M34.017,41.99l-20,0.019c-4.4,0.004-8.003-3.592-8.008-7.992l-0.019-20
c-0.004-4.4,3.592-8.003,7.992-8.008l20-0.019c4.4-0.004,8.003,3.592,8.008,7.992l0.019,20
C42.014,38.383,38.417,41.986,34.017,41.99z"></path><path fill="#fff" d="M24,31c-3.859,
0-7-3.14-7-7s3.141-7,7-7s7,3.14,7,7S27.859,31,24,31z M24,19c-2.757,0-5,2.243-5,5
s2.243,5,5,5s5-2.243,5-5S26.757,19,24,19z"></path><circle cx="31.5" cy="16.5"
r="1.5" fill="#fff"></circle>
<path fill="#fff" d="M30,37H18c-3.859,0-7-3.14-7-7V18c0-3.86,3.141-7,7-7h12c3.859,0,7,3.14,7,7v12 C37,33.86,33.859,37,30,37z M18,13c-2.757,0-5,2.243-5,5v12c0,2.757,2.243,5,5,5h12c2.757,0,5-2.243,5-5V18c0-2.757-2.243-5-5-5H18z"></path>
</svg>
<!-- public\assets\twitter-svgrepo-com.svg -->
<svg xmlns="https://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100"
viewBox="0 0 48 48">
<path fill="#03A9F4" d="M42,12.429c-1.323,0.586-2.746,0.977-4.247,1.162c1.526-0.906,2.7-2.351,3.251-4.058c-1.428,0.837-3.01,1.452-4.693,1.776C34.967,9.884,33.05,9,30.926,9c-4.08,0-7.387,3.278-7.387,7.32c0,0.572,0.067,1.129,0.193,1.67c-6.138-0.308-11.582-3.226-15.224-7.654c-0.64,1.082-1,2.349-1,3.686c0,2.541,1.301,4.778,3.285,6.096c-1.211-0.037-2.351-0.374-3.349-0.914c0,0.022,0,0.055,0,0.086c0,3.551,2.547,6.508,5.923,7.181c-0.617,0.169-1.269,0.263-1.941,0.263c-0.477,0-0.942-0.054-1.392-0.135c0.94,2.902,3.667,5.023,6.898,5.086c-2.528,1.96-5.712,3.134-9.174,3.134c-0.598,0-1.183-0.034-1.761-0.104C9.268,36.786,13.152,38,17.321,38c13.585,0,21.017-11.156,21.017-20.834c0-0.317-0.01-0.633-0.025-0.945C39.763,15.197,41.013,13.905,42,12.429"></path>
</svg>
<!-- public\assets\youtube-svgrepo-com.svg -->
<svg xmlns="https://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100"
viewBox="0 0 48 48">
<path fill="#FF3D00" d="M43.2,33.9c-0.4,2.1-2.1,3.7-4.2,4c-3.3,0.5-8.8,1.1-15,1.1c-6.1,0-11.6-0.6-15-1.1c-2.1-0.3-3.8-1.9-4.2-4C4.4,31.6,4,28.2,4,24c0-4.2,0.4-7.6,0.8-9.9c0.4-2.1,2.1-3.7,4.2-4C12.3,9.6,17.8,9,24,9c6.2,0,11.6,0.6,15,1.1c2.1,0.3,3.8,1.9,4.2,4c0.4,2.3,0.9,5.7,0.9,9.9C44,28.2,43.6,31.6,43.2,33.9z"></path><path fill="#FFF" d="M20 31L20 17 32 24z"></path>
</svg>
Steps to run the application:
Type the command:
npm run devOutput:
.gif)