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

@leafygreen-ui/compound-component

v0.2.0

Published

LeafyGreen UI Kit Compound Component

Readme

Compound Component

npm (scoped)

View on MongoDB.design

Utility functions for creating compound components in React. This package provides factory functions to create components with attached sub-components, following the compound component pattern.

Installation

PNPM

pnpm add @leafygreen-ui/compound-component

Yarn

yarn add @leafygreen-ui/compound-component

NPM

npm install @leafygreen-ui/compound-component

Usage

Basic Compound Component

Use CompoundComponent to create a parent component with attached sub-components:

import {
  CompoundComponent,
  CompoundSubComponent,
} from '@leafygreen-ui/compound-component';

// Create a sub-component
const MySubComponent = CompoundSubComponent(
  ({ children }) => <div className="sub-component">{children}</div>,
  {
    displayName: 'MySubComponent',
    key: 'isMySubComponent',
  },
);

// Create the main compound component
const MyComponent = CompoundComponent(
  ({ children }) => <div className="main-component">{children}</div>,
  {
    displayName: 'MyComponent',
    Sub: MySubComponent,
  },
);

// Usage
function App() {
  return (
    <MyComponent>
      Main content
      <MyComponent.Sub>Sub content</MyComponent.Sub>
    </MyComponent>
  );
}

Multiple Sub-Components

You can attach multiple sub-components to a compound component:

const Header = CompoundSubComponent(
  ({ children }) => <header>{children}</header>,
  {
    displayName: 'Header',
    key: 'isHeader',
  },
);

const Body = CompoundSubComponent(({ children }) => <main>{children}</main>, {
  displayName: 'Body',
  key: 'isBody',
});

const Footer = CompoundSubComponent(
  ({ children }) => <footer>{children}</footer>,
  {
    displayName: 'Footer',
    key: 'isFooter',
  },
);

const Card = CompoundComponent(
  ({ children }) => <div className="card">{children}</div>,
  {
    displayName: 'Card',
    Header,
    Body,
    Footer,
  },
);

// Usage
function App() {
  return (
    <Card>
      <Card.Header>Card Title</Card.Header>
      <Card.Body>Card content goes here</Card.Body>
      <Card.Footer>Card actions</Card.Footer>
    </Card>
  );
}

Finding Sub-Components in Parent Components

Use findChild and findChildren utilities to locate specific sub-components within a parent component's children:

import {
  CompoundComponent,
  CompoundSubComponent,
  findChild,
  findChildren,
} from '@leafygreen-ui/compound-component';

// Define property constants for type safety and consistency
const CardProperties = {
  Header: 'isHeader',
  Body: 'isBody',
  Footer: 'isFooter',
} as const;

// Create sub-components with identifying properties
const Header = CompoundSubComponent(
  ({ children }) => <header>{children}</header>,
  {
    displayName: 'Header',
    key: CardProperties.Header,
  },
);

const Body = CompoundSubComponent(({ children }) => <main>{children}</main>, {
  displayName: 'Body',
  key: CardProperties.Body,
});

const Footer = CompoundSubComponent(
  ({ children }) => <footer>{children}</footer>,
  {
    displayName: 'Footer',
    key: CardProperties.Footer,
  },
);

// Parent component that uses findChild/findChildren
const Card = CompoundComponent(
  ({ children }) => {
    // Find specific sub-components using property constants
    const header = findChild(children, CardProperties.Header);
    const body = findChild(children, CardProperties.Body);
    const footer = findChild(children, CardProperties.Footer);

    // Find all instances of a sub-component type
    const allBodies = findChildren(children, CardProperties.Body);

    return (
      <div className="card">
        {header && <div className="card-header">{header}</div>}
        {body && <div className="card-body">{body}</div>}
        {footer && <div className="card-footer">{footer}</div>}
        {allBodies.length > 1 && (
          <div className="warning">Multiple body sections found!</div>
        )}
      </div>
    );
  },
  {
    displayName: 'Card',
    Header,
    Body,
    Footer,
  },
);

// Usage - parent can control layout and add wrapper elements
function App() {
  return (
    <Card>
      <Card.Header>Card Title</Card.Header>
      <Card.Body>Main content here</Card.Body>
      <Card.Footer>
        <button>Action</button>
      </Card.Footer>
    </Card>
  );
}

Advanced Usage with Conditional Rendering

// Define property constants for the Modal component
const ModalProperties = {
  Header: 'isModalHeader',
  Body: 'isModalBody',
  Footer: 'isModalFooter',
} as const;

const ModalHeader = CompoundSubComponent(
  ({ children }) => <div>{children}</div>,
  {
    displayName: 'ModalHeader',
    key: ModalProperties.Header,
  },
);

const ModalBody = CompoundSubComponent(
  ({ children }) => <div>{children}</div>,
  {
    displayName: 'ModalBody',
    key: ModalProperties.Body,
  },
);

const ModalFooter = CompoundSubComponent(
  ({ children }) => <div>{children}</div>,
  {
    displayName: 'ModalFooter',
    key: ModalProperties.Footer,
  },
);

const Modal = CompoundComponent(
  ({ children }) => {
    // Use property constants instead of string literals
    const header = findChild(children, ModalProperties.Header);
    const body = findChild(children, ModalProperties.Body);
    const footer = findChild(children, ModalProperties.Footer);

    return (
      <div className="modal">
        {/* Only render header section if header component exists */}
        {header && (
          <div className="modal-header">
            {header}
            <button className="close-btn">×</button>
          </div>
        )}

        {/* Body is required */}
        <div className="modal-body">
          {body || <div>No content provided</div>}
        </div>

        {/* Footer is optional */}
        {footer && <div className="modal-footer">{footer}</div>}
      </div>
    );
  },
  {
    displayName: 'Modal',
    Header: ModalHeader,
    Body: ModalBody,
    Footer: ModalFooter,
  },
);

Hierarchical Compound Components (Nested SubComponents)

CompoundSubComponent can accept additional static properties to create hierarchical compound components without needing to nest CompoundComponent calls. This provides a cleaner DX:

import {
  CompoundComponent,
  CompoundSubComponent,
  findChild,
} from '@leafygreen-ui/compound-component';

// Define property constants for each level
const ModalProperties = {
  Header: 'isModalHeader',
  Body: 'isModalBody',
  Footer: 'isModalFooter',
} as const;

const FooterProperties = {
  PrimaryAction: 'isPrimaryAction',
  SecondaryAction: 'isSecondaryAction',
} as const;

// Create the deepest level sub-components
const PrimaryAction = CompoundSubComponent(
  ({ children, ...props }) => (
    <button className="primary-action" {...props}>
      {children}
    </button>
  ),
  {
    displayName: 'PrimaryAction',
    key: FooterProperties.PrimaryAction,
  },
);

const SecondaryAction = CompoundSubComponent(
  ({ children, ...props }) => (
    <button className="secondary-action" {...props}>
      {children}
    </button>
  ),
  {
    displayName: 'SecondaryAction',
    key: FooterProperties.SecondaryAction,
  },
);

// ModalFooter is BOTH a SubComponent AND has its own sub-components
// No need to wrap with CompoundComponent!
const ModalFooter = CompoundSubComponent(
  ({ children }) => {
    // ModalFooter can use findChild for its own children
    const primaryAction = findChild(children, FooterProperties.PrimaryAction);
    const secondaryAction = findChild(
      children,
      FooterProperties.SecondaryAction,
    );

    return (
      <div className="modal-footer">
        {secondaryAction}
        {primaryAction}
      </div>
    );
  },
  {
    displayName: 'ModalFooter',
    key: ModalProperties.Footer,
    // Attach sub-components directly!
    PrimaryAction,
    SecondaryAction,
  },
);

const ModalHeader = CompoundSubComponent(
  ({ children }) => <div className="modal-header">{children}</div>,
  {
    displayName: 'ModalHeader',
    key: ModalProperties.Header,
  },
);

const ModalBody = CompoundSubComponent(
  ({ children }) => <div className="modal-body">{children}</div>,
  {
    displayName: 'ModalBody',
    key: ModalProperties.Body,
  },
);

// Attach to parent Modal component
const Modal = CompoundComponent(
  ({ children }) => {
    const header = findChild(children, ModalProperties.Header);
    const body = findChild(children, ModalProperties.Body);
    const footer = findChild(children, ModalProperties.Footer);

    return (
      <div className="modal">
        {header}
        {body}
        {footer}
      </div>
    );
  },
  {
    displayName: 'Modal',
    Header: ModalHeader,
    Body: ModalBody,
    Footer: ModalFooter,
  },
);

// Usage: Modal.Footer.PrimaryAction works without nesting CompoundComponent!
function App() {
  return (
    <Modal>
      <Modal.Header>Confirm Action</Modal.Header>
      <Modal.Body>Are you sure you want to proceed?</Modal.Body>
      <Modal.Footer>
        <Modal.Footer.SecondaryAction onClick={() => {}}>
          Cancel
        </Modal.Footer.SecondaryAction>
        <Modal.Footer.PrimaryAction onClick={() => {}}>
          Confirm
        </Modal.Footer.PrimaryAction>
      </Modal.Footer>
    </Modal>
  );
}

API

CompoundComponent<Props, Properties>(componentRenderFn, properties)

Creates a compound component with attached sub-components.

Parameters:

  • componentRenderFn: The React component render function
  • properties: Object containing displayName and any sub-components to attach

Returns: A React component with the sub-components attached as static properties

CompoundSubComponent<Key, Props>(componentRenderFn, properties)

Creates a sub-component with a static key property for identification. Can optionally accept additional static properties (like nested sub-components) to create hierarchical compound components.

Parameters:

  • componentRenderFn: The React component render function
  • properties: Object containing:
    • displayName (required): The component display name
    • key (required): The static property name to identify this component
    • Additional properties (optional): Any nested sub-components or other static properties

Returns: A React component with the specified key property set to true and any additional properties attached

findChild(children, staticProperty)

Finds the first child component with a matching static property.

Parameters:

  • children: Any React children (ReactNode)
  • staticProperty: The static property name to check for (string)

Returns: The first matching ReactElement or undefined if not found

Search Depth: Only searches direct children and children inside a single React Fragment level.

findChildren(children, staticProperty)

Finds all child components with a matching static property.

Parameters:

  • children: Any React children (ReactNode)
  • staticProperty: The static property name to check for (string)

Returns: Array of matching ReactElements (empty array if none found)

Search Depth: Only searches direct children and children inside a single React Fragment level.

Key Property

Sub-components created with CompoundSubComponent have a static property (specified by the key parameter) set to true. This can be used for component identification:

const MySubComponent = CompoundSubComponent(() => <div />, {
  displayName: 'MySubComponent',
  key: 'isMySubComponent',
});

console.log(MySubComponent.isMySubComponent); // true

Note: The key property itself is not exposed on the component to avoid conflicts with React's built-in key prop.