Search code examples
cssnext.jstailwind-css

How to make a section sticky using Tailwind CSS utility classes


In my Next.js web app, I have a page as shown below:

enter image description here

I want to make the section (wrapped in <aside> tag) highlighted in blue sticky, so that when I scroll it stays where it is and only the main section (the section containing the chart) scrolls.

Here is the layout.tsx file:

import { dashboardConfig } from "@/config/dashboard";
import { MainNav } from "@/components/nav/main-nav";
import { DashboardNav } from "@/components/nav/dashboard-nav";

interface DashboardLayoutProps {
  children?: React.ReactNode;
}

export default async function DashboardLayout({
  children,
}: DashboardLayoutProps) {
  return (
    <div className="flex min-h-dvh flex-col relative">
      <header className="container z-40 bg-background">
        <MainNav />
      </header>
      <div className="container grid flex-1 gap-12 md:grid-cols-[200px_1fr] mt-32 mb-12 relative">
        <aside className="hidden w-[200px] flex-col md:flex sticky top-0">
          <DashboardNav items={dashboardConfig.sidebarNav} />
        </aside>
        <main className="flex w-full flex-1 flex-col overflow-hidden">
          {children}
        </main>
      </div>
    </div>
  );
}

Notice that I have applied classes sticky and top-0 to the section I want to have the position sticky. But it's not working.

What am I doing wrong?


Solution

  • Consider applying align-self: start via self-start to the sticky element. By default, it would have align-self: stretch which would make it the full height of its parent grid element, thus no sticky effect is observed. By applying align-self: start, it will only be as tall as its content, allowing a sticky effect to be observed if there is vertical free space.

    const dashboardConfig = { sidebarNav: '' };
    const MainNav = () => 'MainNav';
    const DashboardNav = () => 'DashboardNav';
    
    function DashboardLayout({
      children,
    }) {
      return (
        <div className="flex min-h-dvh flex-col relative">
          <header className="container z-40 bg-background">
            <MainNav />
          </header>
          <div className="container grid flex-1 gap-12 md:grid-cols-[200px_1fr] mt-32 mb-12 relative">
            <aside className="hidden w-[200px] flex-col md:flex sticky top-0 self-start">
              <DashboardNav items={dashboardConfig.sidebarNav} />
            </aside>
            <main className="flex w-full flex-1 flex-col overflow-hidden">
              {children}
            </main>
          </div>
        </div>
      );
    }
    
    function App() {
      return (
        <DashboardLayout>
          <div class="h-[200vh]"></div>
        </DashboardLayout>
      );
    }
    
    ReactDOM.createRoot(document.getElementById('app')).render(<App/>);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js" integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js" integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdn.tailwindcss.com/3.4.1"></script>
    
    <div id="app"></div>