Open In App

Social Media Dashboard With NextJS

Last Updated : 23 Jul, 2025
Suggest changes
Share
Like Article
Like
Report

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:

Screenshot-2024-02-15-223824
Social Media Dashboard With Next.js

Prerequisites:

Approach

  1. Setting up Next.js Environment: Install Node.js and set up a Next.js project using create-next-app.
  2. Implement Authentication: Integrate authentication using NextAuth.js and OAuth for social media platforms.(Optional for now).
  3. Connect Social Media APIs: Utilize APIs of social media platforms for fetching data, posting content, and managing accounts.(Further Enhancement work)
  4. 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

Screenshot-2024-02-12-171804
Project Structure
You can put any logo you need in the public\assets directory

Dependencies

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.

JavaScript
// 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 
JavaScript
// 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 
JavaScript
// 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; 
JavaScript
// 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 
JavaScript
// 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 
JavaScript
// 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 
JavaScript
// src\pages\_app.js import '@/styles/globals.css' export default function App({ Component, pageProps }) {  return <Component {...pageProps} /> } 
JavaScript
// 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>  ) } 
JavaScript
// 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,  },  }; } 
XML
<!-- 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> 
XML
<!-- 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> 
XML
<!-- 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> 
XML
<!-- 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> 
XML
<!-- 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> 
XML
<!-- 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 dev

Output:

test-(1)
Social Media Dashboard With Next.js

Explore