Examples

This page contains various uses of Overflowed and it’s implementation in each framework. You can resize the container by dragging the handle on the right side of each example.

Toolbar

This example shows how an application toolbar could be implemented.

Source code
import { useOverflowedItems } from "overflowed/react";

export const ToolbarExample = () => {
  const [visibleToolbarItems, overflowedToolbarItems, { getContainerProps, getIndicatorProps }] =
    useOverflowedItems(toolbarItems);

  return (
    <Toolbar {...getContainerProps()}>
      {visibleToolbarItems.map(([link, getProps], index) => (
        <MenuTrigger key={index}>
          <Button {...getProps()}>{link.label}</Button>
          <Popover>
            <Menu>
              {link.children.map((childItem, index) => (
                <MenuItem key={index}>{childItem.label}</MenuItem>
              ))}
            </Menu>
          </Popover>
        </MenuTrigger>
      ))}
      <MenuTrigger>
        <Button {...getIndicatorProps()}>More</Button>
        <Popover>
          <Menu>
            {overflowedToolbarItems.map((toolbarItem, index) => (
              <SubmenuTrigger key={index}>
                <MenuItem>
                  {toolbarItem.label} <TriangleRightIcon />
                </MenuItem>
                <Popover>
                  <Menu>
                    {toolbarItem.children.map((toolbarItemChild, index) => (
                      <MenuItem key={index}>{toolbarItemChild.label}</MenuItem>
                    ))}
                  </Menu>
                </Popover>
              </SubmenuTrigger>
            ))}
          </Menu>
        </Popover>
      </MenuTrigger>
    </Toolbar>
  );
};

Avatar groups

This example shows a live list of GitHub stargazers as avatars. The end indicator shows how many avatars are currently hidden. Feel free to add yourself 🥰.

Starred by 40

  • CP
  • WH
  • EV
  • MP
  • PA
  • SS
  • RDB
  • OO
  • SM
  • DT
  • ER
  • JI
  • ES
  • FM
  • OG
  • FP
  • LS
  • JJ
  • MC
  • FW
  • CP
  • WH
  • EV
  • MP
  • PA
  • SS
  • RDB
  • OO
  • SM
  • DT
  • ER
  • JI
  • ES
  • FM
  • OG
  • FP
  • LS
  • JJ
  • MC
  • FW
Source code
import { useOverflowedItems } from "overflowed/react";

export const AvatarExample = () => {
  const [visibleStargazers, overflowedStargazers, { getContainerProps, getIndicatorProps }] =
    useOverflowedItems(stargazers);

  return (
    <div>
      <h2>
        Starred by <span>{stargazers.length}</span>
      </h2>
      <ul {...getContainerProps()}>
        {visibleStargazers.map(([stargazer, getItemProps]) => (
          <li key={stargazer.id} {...getItemProps({ style: { backgroundColor: stargazer.color } })}>
            {stargazer.initials}
          </li>
        ))}
        <li {...getIndicatorProps()}>+{overflowedStargazers.length}</li>
      </ul>
    </div>
  );
};

In this example the navigation bar has too many items to fit on a single line, so we add the overflowing options into a dropdown menu.

Source code
import { useOverflowedItems } from "overflowed/react";

import styles from "./_NavigationExample.module.css";

import { links } from "./_data";

export const NavigationExample = () => {
  const [visibleLinks, overflowedLinks, { getContainerProps, getIndicatorProps }] = useOverflowedItems(links);

  return (
    <ul className={styles["container"]} {...getContainerProps()}>
      {visibleLinks.map(([link, getProps]) => (
        <li key={link.id} {...getProps()}>
          <a href={link.url} onClick={(event) => event.preventDefault()}>
            {link.title}
          </a>
        </li>
      ))}
      <li {...getIndicatorProps()}>More</li>
    </ul>
  );
};

Transitions

This examples showcases items fading in/out and the indicator smoothly moving around. This is simply acheived with CSS and allows you write styling like you normally would.

Source code
import { useOverflowedItems } from "overflowed/react";

export const PlaylistExample = () => {
  const [visibleSongs, overflowedSongs, { getContainerProps, getIndicatorProps }] = useOverflowedItems(songs);
  const [isExpanded, setIsExpanded] = useState(false);

  return (
    <article>
      <header>
        <h1>Overflowed</h1>
        <h2>Playlist</h2>
      </header>
      <ul {...getContainerProps()}>
        {visibleSongs.map(([song, getItemProps]) => (
          <li key={song.id} {...getItemProps()}>
            <a href={song.spotifyLink} target="_blank">
              <article>
                <img src={song.coverArt} alt="Cover Art" />
                <header>
                  <h1>{song.title}</h1>
                  <h2>{song.artist}</h2>
                </header>
              </article>
            </a>
          </li>
        ))}
        <li data-count={overflowedSongs.length} {...getIndicatorProps()}>
          <button onClick={() => setIsExpanded(true)} data-text={`+${overflowedSongs.length}`}>
            {overflowedSongs.slice(0, 4).map((song) => (
              <img key={song.id} src={song.coverArt} alt="Cover Art" />
            ))}
          </button>
        </li>
        {isExpanded && (
          <li>
            <button onClick={() => setIsExpanded(false)} data-text="⨉" />
          </li>
        )}
      </ul>
    </article>
  );
};

Animations

This example demonstrates Overflowed’s ability to adapt to lists with moving or animated elements. The letters are constantly resizing and the library is able to keep up with these changes at 60FPS.

TODO: improve performance