Index: frontend/src/pages/Dashboard/components/AddCustomCategoryModal.jsx
===================================================================
--- frontend/src/pages/Dashboard/components/AddCustomCategoryModal.jsx	(revision b3d5fed65cdab945e6c84835e2e04bc1814d9699)
+++ frontend/src/pages/Dashboard/components/AddCustomCategoryModal.jsx	(revision b3d5fed65cdab945e6c84835e2e04bc1814d9699)
@@ -0,0 +1,101 @@
+import React, { useEffect, useMemo, useState } from "react";
+import { createPortal } from "react-dom";
+
+export default function AddCustomCategoryModal({ open, onClose, onSubmit }) {
+  const [name, setName] = useState("");
+  const [submitting, setSubmitting] = useState(false);
+  const [error, setError] = useState(null);
+
+  // Reset the form whenever the modal is opened.
+  // NOTE: Hooks must be called in the same order on every render; do not early-return before hooks.
+  useEffect(() => {
+    if (!open) return;
+    setName("");
+    setError(null);
+    setSubmitting(false);
+  }, [open]);
+
+  const canSubmit = useMemo(() => {
+    return name.trim().length > 0 && name.trim().length <= 100 && !submitting;
+  }, [name, submitting]);
+
+  async function handleSubmit(e) {
+    e.preventDefault();
+    if (!canSubmit) return;
+
+    setSubmitting(true);
+    setError(null);
+    try {
+      await onSubmit({ name: name.trim() });
+      onClose?.();
+    } catch (err) {
+      setError(
+        err?.response?.data?.message ||
+          err?.message ||
+          "Failed to create custom category"
+      );
+      setSubmitting(false);
+    }
+  }
+
+  if (!open) return null;
+
+  return createPortal(
+    <div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
+      <button
+        type="button"
+        className="absolute inset-0 bg-black/60"
+        aria-label="Close modal"
+        onClick={onClose}
+      />
+
+      <div className="relative w-full max-w-md rounded-xl bg-neutral-900 p-5 text-white shadow-xl">
+        <div className="flex items-center justify-between gap-4">
+          <h2 className="text-lg font-semibold">Add Custom Category</h2>
+          <button
+            type="button"
+            onClick={onClose}
+            className="rounded-md px-2 py-1 text-sm text-white/80 hover:bg-white/10"
+          >
+            Close
+          </button>
+        </div>
+
+        <form onSubmit={handleSubmit} className="mt-4 space-y-3">
+          <div>
+            <label className="mb-1 block text-sm text-white/80">Name</label>
+            <input
+              value={name}
+              onChange={(e) => setName(e.target.value)}
+              maxLength={100}
+              autoFocus
+              className="w-full rounded-lg border border-white/10 bg-black/40 px-3 py-2 text-sm outline-none focus:border-green-400"
+              placeholder="e.g. My Habit Tracking"
+            />
+            <div className="mt-1 text-xs text-white/50">
+              {name.trim().length}/100
+            </div>
+          </div>
+
+          {error ? (
+            <div className="rounded-lg border border-red-500/30 bg-red-500/10 p-2 text-sm text-red-200">
+              {error}
+            </div>
+          ) : null}
+
+          <button
+            type="submit"
+            disabled={!canSubmit}
+            className="w-full rounded-lg bg-green-400 px-3 py-2 text-sm font-medium text-black disabled:cursor-not-allowed disabled:opacity-60"
+          >
+            {submitting ? "Creating..." : "Create"}
+          </button>
+        </form>
+      </div>
+    </div>,
+    document.body
+  );
+}
+
+
+
Index: frontend/src/pages/Dashboard/components/DashboardSidebar.jsx
===================================================================
--- frontend/src/pages/Dashboard/components/DashboardSidebar.jsx	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ frontend/src/pages/Dashboard/components/DashboardSidebar.jsx	(revision b3d5fed65cdab945e6c84835e2e04bc1814d9699)
@@ -1,3 +1,3 @@
-import React from "react";
+import React, { useEffect, useMemo, useState } from "react";
 import { NavLink, useLocation } from "react-router-dom";
 
@@ -20,7 +20,19 @@
   MdChecklist,
   MdAdd,
+  MdBook,
+  MdFlag,
+  MdStar,
+  MdBolt,
+  MdNoteAlt,
+  MdAutoGraph,
 } from "react-icons/md";
 
 import logo from "../../../assets/logo.png";
+
+import AddCustomCategoryModal from "./AddCustomCategoryModal.jsx";
+import {
+  createCustomTrackingCategory,
+  getCustomTrackingCategories,
+} from "../../../api/discipline.js";
 
 const navItems = [
@@ -33,6 +45,45 @@
 ];
 
+const CUSTOM_ICONS = [MdBook, MdFlag, MdStar, MdBolt, MdNoteAlt, MdAutoGraph];
+
+function stableIconForId(id) {
+  const n = Number(id);
+  if (!Number.isFinite(n)) return CUSTOM_ICONS[0];
+  return CUSTOM_ICONS[Math.abs(n) % CUSTOM_ICONS.length];
+}
+
 const DashboardSidebar = () => {
   const location = useLocation();
+  const [customOpen, setCustomOpen] = useState(false);
+  const [customCategories, setCustomCategories] = useState([]);
+
+  useEffect(() => {
+    let mounted = true;
+    (async () => {
+      try {
+        const res = await getCustomTrackingCategories();
+        const items = res?.items ?? [];
+        if (mounted) setCustomCategories(items);
+      } catch {
+        // If the user isn't authenticated yet or the endpoint fails,
+        // keep the sidebar usable (no custom categories).
+        if (mounted) setCustomCategories([]);
+      }
+    })();
+    return () => {
+      mounted = false;
+    };
+  }, []);
+
+  const customNavItems = useMemo(() => {
+    return (customCategories ?? []).map((c) => {
+      const Icon = stableIconForId(c.customTrackingId);
+      return {
+        title: c.name,
+        to: `/dashboard/custom/${c.customTrackingId}`,
+        icon: Icon,
+      };
+    });
+  }, [customCategories]);
 
   return (
@@ -52,4 +103,46 @@
           {navItems.map((item) => (
             <SidebarMenuItem key={item.title}>
+              {(() => {
+                const isActive =
+                  location.pathname === item.to ||
+                  location.pathname.startsWith(`${item.to}/`);
+
+                return (
+                  <SidebarMenuButton
+                    asChild
+                    isActive={isActive}
+                    className={
+                      isActive
+                        ? "!bg-green-400 !text-black hover:!bg-green-400 active:!bg-green-400"
+                        : ""
+                    }
+                  >
+                    <NavLink
+                      to={item.to}
+                      className="flex w-full items-center gap-3"
+                    >
+                      <item.icon
+                        className={
+                          isActive
+                            ? "size-6 shrink-0 text-black"
+                            : "size-6 shrink-0"
+                        }
+                      />
+                      <span className={isActive ? "text-black" : ""}>
+                        {item.title}
+                      </span>
+                    </NavLink>
+                  </SidebarMenuButton>
+                );
+              })()}
+            </SidebarMenuItem>
+          ))}
+
+          {customNavItems.length > 0 ? (
+            <div className="my-4 border-t border-white/10" />
+          ) : null}
+
+          {customNavItems.map((item) => (
+            <SidebarMenuItem key={`custom-${item.to}`}>
               {(() => {
                 const isActive =
@@ -94,4 +187,5 @@
             className="w-full justify-start gap-2 !bg-green-400 !text-black hover:!bg-green-500"
             type="button"
+            onClick={() => setCustomOpen(true)}
           >
             <MdAdd className="size-5" />
@@ -99,4 +193,13 @@
           </Button>
         </div>
+
+        <AddCustomCategoryModal
+          open={customOpen}
+          onClose={() => setCustomOpen(false)}
+          onSubmit={async (payload) => {
+            const created = await createCustomTrackingCategory(payload);
+            setCustomCategories((prev) => [created, ...(prev ?? [])]);
+          }}
+        />
       </SidebarContent>
     </Sidebar>
