1 GitHub star = 1 new component.

Star the repo
Layoutlayoutstickysidebar

Sticky Side Column

Sidebar container that sticks while main content scrolls.

Live Preview

Usage

Copy and paste this code into your React component:

tsx
import { StickySidebarWrapper } from 'react-component-kit-ui/layout';

<StickySidebarWrapper>Content</StickySidebarWrapper>

Manual Installation

If you are not using the npm package, create a new file and paste the following code: src/components/StickySidebarWrapper.tsx + StickySidebarWrapper.css:

StickySidebarWrapper.tsx
'use client';

import type { CSSProperties, ReactNode } from 'react';
import './StickySidebarWrapper.css';

/** class:list β†’ className */
function cx(...args: unknown[]): string {
  const out: string[] = [];
  const walk = (v: unknown) => {
    if (!v) return;
    if (typeof v === 'string' || typeof v === 'number') out.push(String(v));
    else if (Array.isArray(v)) v.forEach(walk);
    else if (typeof v === 'object') for (const [k, on] of Object.entries(v as Record<string, unknown>)) if (on) out.push(k);
  };
  args.forEach(walk);
  return out.join(' ');
}

/** "a: b; --c: d" β†’ style object */
function st(css: string | undefined): CSSProperties {
  const o: Record<string, string> = {};
  for (const decl of String(css ?? '').split(';')) {
    const i = decl.indexOf(':');
    if (i < 0) continue;
    const k = decl.slice(0, i).trim();
    if (k) o[k.startsWith('--') ? k : k.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = decl.slice(i + 1).trim();
  }
  return o as CSSProperties;
}

/**
 * StickySidebarWrapper β€” A two-column layout where the sidebar content stays visible while scrolling.
 *
 * Creates a main + sidebar layout where the sidebar column uses CSS sticky positioning
 * to remain visible as the user scrolls through the main content. Stacks vertically on mobile.
 *
 * @param {string} sidebarWidth - Width of the sidebar. Default: '280px'.
 * @param {string} stickyTop - Distance from the top when sticky. Default: '100px'.
 * @param {string} gap - Gap between main and sidebar columns. Default: '2rem'.
 * @param {string} className - Additional CSS class to apply.
 *
 * @example
 * ```astro
 * <StickySidebarWrapper sidebarWidth="260px" stickyTop="120px">
 *   <div slot="main">
 *     <p>Long scrolling content...</p>
 *   </div>
 *   <div slot="sidebar">
 *     <TableOfContents />
 *   </div>
 * </StickySidebarWrapper>
 * ```
 */
export interface StickySidebarWrapperProps {
  sidebarWidth?: string;
  stickyTop?: string;
  gap?: string;
  className?: string;
  main?: ReactNode;
  sidebar?: ReactNode;
}

export function StickySidebarWrapper({ sidebarWidth = '280px', stickyTop = '100px', gap = '2rem', className = '',
  main,
  sidebar
}: StickySidebarWrapperProps) {
  return (
    <>
      <div className="cq-sticky-sidebar-wrapper-cq" data-rk-0549ef="">
      <div className={cx(['sticky-sidebar', className])} style={st(`--sidebar-w: ${sidebarWidth}; --sticky-top: ${stickyTop}; --col-gap: ${gap}`)} data-rk-0549ef="">
        <main className="sticky-sidebar__main" data-rk-0549ef="">
          {main}
        </main>
        <aside className="sticky-sidebar__aside" data-rk-0549ef="">
          <div className="sticky-sidebar__inner" data-rk-0549ef="">
            {sidebar}
          </div>
        </aside>
      </div>
      </div>
    </>
  );
}

export default StickySidebarWrapper;
StickySidebarWrapper.css
/* StickySidebarWrapper β€” ported from astro-component-kit (layout/StickySidebarWrapper.astro) */

.cq-sticky-sidebar-wrapper-cq:where([data-rk-0549ef]) { container-type: inline-size; }
.sticky-sidebar:where([data-rk-0549ef]) {
  display: grid;
  grid-template-columns: 1fr var(--sidebar-w, 280px);
  gap: var(--col-gap, 2rem);
  align-items: start;
}
.sticky-sidebar__main:where([data-rk-0549ef]) {
  min-width: 0;
}
.sticky-sidebar__aside:where([data-rk-0549ef]) {
  min-width: 0;
}
.sticky-sidebar__inner:where([data-rk-0549ef]) {
  position: sticky;
  top: var(--sticky-top, 100px);
}

@container (max-width: 768px) {
  .sticky-sidebar:where([data-rk-0549ef]) {
    grid-template-columns: 1fr;
    gap: var(--sp-6);
  }
  .sticky-sidebar__inner:where([data-rk-0549ef]) {
    position: static;
  }
}

Quick Info

Category
Layout
Filename
StickySidebarWrapper.tsx
Dependencies
None β€” pure React + CSS
Tags
layoutstickysidebar