How to Create a Sidebar in NextJS & Tailwind CSS?

In web development, sidebars are commonly used to organize navigation elements and provide quick access to different sections of a website. A sidebar is a vertical menu or panel that is typically positioned on the left or right side of the main content area.

Prerequisites:

Steps to Create NextJS Application:

Step 1: Create a nextJS application by using this command

npx create-next-app my-app

Step 2: Install Tailwind CSS by running this command

npm install tailwindcss

Step 3: Configure Tailwind CSS by creating a ‘tailwind.config.js’ file and adding any customization if needed.

Project Structure:

The updated dependencies in package.json file will look like.

"dependencies": {
    "react": "^18",
    "react-dom": "^18",
    "next": "14.2.3"
  },
  "devDependencies": {
    "postcss": "^8",
    "tailwindcss": "^3.4.1"
  }

Table of Content

  • Using Tailwind CSS utilities directly
  • Using Custom CSS with Tailwind CSS Utility Classes

Using Tailwind CSS utilities directly

  • This approach involves using Tailwind CSS utility classes directly in your components to style and position the sidebar.
  • In this approach you not need to create separate CSS files, all the styling is done inline with the help of utility classes.
  • This approach provides maximum flexibility but requires more manual styling effort.

Example: This example shows the use of the above-explained approach.

JavaScript
//Sidebar.js
import React, { useState } from 'react';

const Sidebar = () => {
  // State to manage the open/close state of the sidebar
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div className="flex">
      {/* Sidebar */}
      <div
        // Conditional class based on isOpen 
        // state to control width and visibility
        className={`bg-gray-800 text-white 
                    fixed h-screen transition-all 
                    duration-300 z-10 
                    ${isOpen ? 'w-64' : 'w-0 overflow-hidden'
          }`}>
        {/* Sidebar content */}
        <div className="flex flex-col items-center">
          <div className="mt-4">
            <a href="#"
              className="text-white 
                          hover:text-gray-300">
              Home
            </a>
          </div>
          <div className="mt-4">
            <a href="#"
              className="text-white 
                          hover:text-gray-300">
              About
            </a>
          </div>
          {/* Add more sidebar items here */}
        </div>
      </div>
      {/* Main content */}
      <div className={`flex-1 p-4 
                        ${isOpen ? 'ml-64' : 'ml-0'}`}>
        {/* Button to toggle sidebar */}
        <div className="ml-auto">
          <button
            className="bg-blue-500 hover:bg-blue-700 
                       text-white font-bold py-2 px-4 rounded"
            onClick={() => setIsOpen(!isOpen)}>
            {/* Toggle icon based on isOpen state */}
            {isOpen ? (
              <svg
                className="h-6 w-6"
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor">
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M6 18L18 6M6 6l12 12" />
              </svg>
            ) : (
              <svg
                className="h-6 w-6"
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor">
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M4 6h16M4 12h16m-7 6h7" />
              </svg>
            )}
          </button>
        </div>
      </div>
    </div>
  );
};

export default Sidebar;
JavaScript
// pages/index.js

import React from 'react';
import Sidebar from './components/Sidebar';

const HomePage = () => {
  return (
    <div>
      {/* Sidebar component */}
      <Sidebar />
      {/* Main content of the homepage */}
      <div className="p-4">
        {/* Title */}
        <h1 className="text-3xl font-bold mb-4"></h1>
        {/* Additional content */}
        <p></p>
      </div>
    </div>
  );
};

export default HomePage;

Output:

Using Custom CSS with Tailwind CSS Utility Classes

  • In this approach we create a reusable React component for the sidebar, encapsulating its logic and styling.
  • You can create a separate CSS module file to define the styles for the sidebar component.
  • This approach promotes the code reusability and modularity and making it easier it maintain and use the sidebar throughout your application

Example: This example shows the use of the above-explained approach.

CSS
/* styles/components/Sidebar.module.css */
.container {
    height: 100vh;
    position: relative;
}

.sidebar {
    position: fixed;
    top: 0;
    left: -5rem;
    height: 100vh;
    background-color: #1a202c;
    color: #fff;
    transition: width 0.3s ease-in-out, left 0.3s ease-in-out;
}

.sidebarOpen {
    width: 16rem;
    left: 0;
}

.sidebarClosed {
    width: 0;
}

.content {
    margin-left: 5rem;
    transition: margin-left 0.3s ease-in-out;
}

.contentShifted {
    margin-left: calc(5rem + var(--sidebar-width) + 40px);
}

.contentShiftedBack {
    margin-left: 40px;
}

.toggleButton {
    background-color: #4299e1;
    color: #fff;
    font-weight: bold;
    padding: 0.5rem 1rem;
    border-radius: 0.25rem;
    position: absolute;
    top: 20px;
    z-index: 1;
    transition: left 0.3s ease-in-out;
}

.nav {
    margin-top: 50px;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    padding: 1rem;
}
JavaScript
//Sidebar.js
import React, { useState, useEffect } from 'react';
import styles from '/styles/components/Sidebar.module.css';

const Sidebar = () => {
  // State for managing sidebar open/close state and width
  const [isOpen, setIsOpen] = useState(false);
  const [sidebarWidth, setSidebarWidth] = useState(16);

  // Effect to update sidebar width when isOpen state changes
  useEffect(() => {
    // Get sidebar element
    const sidebarElement =
      document.querySelector(`.${styles.sidebar}`);
    if (sidebarElement) {
      // Calculate width based on isOpen state
      const width =
        isOpen ? sidebarElement.offsetWidth : 0;
      setSidebarWidth(width);
    }
  }, [isOpen]);

  return (
    <div className={`${styles.container} flex`}>
      {/* Sidebar */}
      <div
        className={`${styles.sidebar} 
                    ${isOpen ? styles.sidebarOpen : styles.sidebarClosed
          }`}
        // Adjusted width based on isOpen state
        style={{ width: isOpen ? '16rem' : '5rem' }}
      >
        {/* Sidebar content */}
        <ul className={styles.nav}>
          <li><a href="#">Home</a></li>
          <li><a href="#">About</a></li>
        </ul>
      </div>
      {/* Main content */}
      <div
        className={`${styles.content} 
                  flex-1 p-4 
                  ${isOpen ? styles.contentShifted : styles.contentShiftedBack
          }`}>
        {/* Button to toggle sidebar */}
        <button
          className={styles.toggleButton}
          onClick={() => setIsOpen(!isOpen)}
          style={{
            // Adjusted button position based on sidebar width
            left: isOpen ? `calc(${sidebarWidth}px + 20px)` : '20px'
          }} >
          {/* Toggle icon based on isOpen state */}
          {isOpen ? (
            <svg className="h-6 w-6" fill="none"
              viewBox="0 0 24 24" stroke="currentColor">
              <path strokeLinecap="round" strokeLinejoin="round"
                strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
            </svg>
          ) : (
            <svg className="h-6 w-6" fill="none"
              viewBox="0 0 24 24" stroke="currentColor">
              <path strokeLinecap="round" strokeLinejoin="round"
                strokeWidth={2} d="M4 6h16M4 12h16m-7 6h7" />
            </svg>
          )}
        </button>
      </div>
    </div>
  );
};

export default Sidebar;
JavaScript
// pages/index.js

import React from 'react';
import Sidebar from './components/Sidebar';

const HomePage = () => {
  return (
    <div>
      {/* Sidebar component */}
      <Sidebar />
      {/* Main content of the homepage */}
      <div className="p-4">
        {/* Title */}
        <h1 className="text-3xl font-bold mb-4"></h1>
        {/* Additional content */}
        <p></p>
      </div>
    </div>
  );
};

export default HomePage;

Output: