npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lg-chat/chat-layout

v0.2.3

Published

LeafyGreen UI Kit Chat Layout

Readme

Chat Layout

npm (scoped)

View on MongoDB.design

Installation

PNPM

pnpm add @lg-chat/chat-layout

Yarn

yarn add @lg-chat/chat-layout

NPM

npm install @lg-chat/chat-layout

Overview

@lg-chat/chat-layout provides a CSS Grid-based layout system for building full-screen chat interfaces with a side nav that can be collapsed or pinned.

This package exports:

  • ChatLayout: The grid container and context provider
  • ChatMain: The primary content area of the chat interface, automatically positioned within the grid layout.
  • ChatSideNav: A compound component representing the side navigation, exposing subcomponents such as ChatSideNav.Header, ChatSideNav.Content, and ChatSideNav.SideNavItem for flexible composition.
  • useChatLayoutContext: Hook for accessing layout state

Examples

Basic

import { useState } from 'react';
import { ChatLayout, ChatMain, ChatSideNav } from '@lg-chat/chat-layout';

function MyChatApp() {
  const [activeChatId, setActiveChatId] = useState('1');

  const chatItems = [
    { id: '1', name: 'MongoDB Atlas Setup', href: '/chat/1' },
    { id: '2', name: 'Database Query Help', href: '/chat/2' },
    { id: '3', name: 'Schema Design Discussion', href: '/chat/3' },
  ];

  const handleNewChat = () => {
    console.log('Start new chat');
  };

  return (
    <ChatLayout>
      <ChatSideNav>
        <ChatSideNav.Header onClickNewChat={handleNewChat} />
        <ChatSideNav.Content>
          {chatItems.map(({ href, id, item, name }) => (
            <ChatSideNav.SideNavItem
              key={id}
              href={href}
              active={id === activeChatId}
              onClick={e => {
                e.preventDefault();
                setActiveChatId(id);
              }}
            >
              {name}
            </ChatSideNav.SideNavItem>
          ))}
        </ChatSideNav.Content>
      </ChatSideNav>
      <ChatMain>{/* Main chat content here */}</ChatMain>
    </ChatLayout>
  );
}

With Initial State and Toggle Pinned Callback

import { ChatLayout, ChatMain, ChatSideNav } from '@lg-chat/chat-layout';

function MyChatApp() {
  const handleTogglePinned = (isPinned: boolean) => {
    console.log('Side nav is now:', isPinned ? 'pinned' : 'collapsed');
  };

  return (
    <ChatLayout initialIsPinned={false} onTogglePinned={handleTogglePinned}>
      <ChatSideNav>{/* Side nav subcomponents */}</ChatSideNav>
      <ChatMain>{/* Main chat content */}</ChatMain>
    </ChatLayout>
  );
}

Properties

ChatLayout

| Prop | Type | Description | Default | | ------------------------------ | ----------------------------- | --------------------------------------------------------------------------------------------- | ------- | | children | ReactNode | The content to render inside the grid layout (ChatSideNav and ChatMain components) | - | | className (optional) | string | Custom CSS class to apply to the grid container | - | | initialIsPinned (optional) | boolean | Initial state for whether the side nav is pinned (expanded) | true | | onTogglePinned (optional) | (isPinned: boolean) => void | Callback fired when the side nav is toggled. Receives the new isPinned state as an argument | - |

All other props are passed through to the underlying <div> element.

ChatMain

| Prop | Type | Description | Default | | ---------- | ----------- | -------------------------- | ------- | | children | ReactNode | The main content to render | - |

All other props are passed through to the underlying <div> element.

Note: ChatMain must be used as a direct child of ChatLayout to work correctly within the grid system.

ChatSideNav

| Prop | Type | Description | Default | | ------------------------ | ------------------------- | -------------------------------------------------------------- | ------- | | children | ReactNode | Should include ChatSideNav.Header and ChatSideNav.Content. | - | | className (optional) | string | Root class name | - | | ... | HTMLElementProps<'nav'> | Props spread on the root <nav> element | - |

ChatSideNav.Header

| Prop | Type | Description | Default | | ----------------------------- | -------------------------------------- | ------------------------------------------- | ------- | | onClickNewChat (optional) | MouseEventHandler<HTMLButtonElement> | Fired when the "New Chat" button is clicked | - | | className (optional) | string | Header class name | - | | ... | HTMLElementProps<'div'> | Props spread on the header container | - |

ChatSideNav.Content

| Prop | Type | Description | Default | | ------------------------ | ------------------------- | ------------------------------------- | ------- | | className (optional) | string | Content class name | - | | ... | HTMLElementProps<'div'> | Props spread on the content container | - |

ChatSideNav.SideNavItem

| Prop | Type | Description | Default | | ------------------------ | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | active (optional) | boolean | Whether or not the component should be rendered in an active state. When active, applies active styling and sets aria-current="page" | false | | as (optional) | React.ElementType | When provided, the component will be rendered as the component or html tag indicated by this prop. Other additional props will be spread on the element. For example, Link or a tags can be supplied. Defaults to 'a' | - | | children | ReactNode | Content that will be rendered inside the root-level element (typically the chat name) | - | | className (optional) | string | Class name that will be applied to the root-level element | - | | href (optional) | string | The URL that the hyperlink points to. When provided, the component will be rendered as an anchor element | - | | onClick (optional) | MouseEventHandler | The event handler function for the 'onclick' event. Receives the associated event object as the first argument | - |

Context API

useChatLayoutContext

Hook that returns the current chat layout context:

const {
  isPinned,
  togglePin,
  isSideNavHovered,
  setIsSideNavHovered,
  shouldRenderExpanded,
} = useChatLayoutContext();

Returns:

| Property | Type | Description | | ---------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------- | | isPinned | boolean | Whether the side nav is currently pinned | | togglePin | () => void | Function to toggle the pinned state | | isSideNavHovered | boolean | Whether the side nav is currently being hovered | | setIsSideNavHovered | (isHovered: boolean) => void | Function to set the hover state of the side nav | | shouldRenderExpanded | boolean | Whether the side nav should render in expanded state. This is true when the nav is pinned OR hovered. |

Behavior

State Management

  • ChatLayout manages the isPinned and isSideNavHovered state internally and provides it to all descendants via ChatLayoutContext
  • shouldRenderExpanded is computed as isPinned || isSideNavHovered and provided in the context for convenience
  • When togglePin is called:
    1. The isPinned state updates
    2. Grid columns resize smoothly via CSS transition
    3. The onTogglePinned callback fires (if provided) with the new state value
  • Descendant components can consume the context to:
    • Read the current isPinned state
    • Call togglePin() to toggle the sidebar
    • Read the current isSideNavHovered state
    • Call setIsSideNavHovered() to update the hover state
    • Use shouldRenderExpanded to determine if the side nav should render in expanded state