LogoCommand Menu

Headless UI for building
command menus in React.

  • Favorites
    • Spotify
      Control Spotify
    • Instagram
      Check our instagram
    • Twitter
      Check our twitter
  • Common
    • Weather
      Check today's weather
    • Share
      Share smth
    • Github
      Check github
    • Framer
      Open Framer
    • Figma
      Open Figma
    • Light mode
      Switch to light mode
    • Dark mode
      Switch to dark mode
    • Configuration
      Change smth

How to use it?

This package offers a hook for creating a customized command menu. It returns an object that includes properties for every element in the menu, such as the menu itself, the search input, and a list of all the necessary properties for each menu item which enables you to easily build a command menu tailored to your specific needs.

1import { useCommandMenu } from "commandmenu";
2import { config } from "./config";
3  
4const { selectedItem, selectedItemRef, menuProps, searchProps, list } = 
5  useCommandMenu({ config })

In order to fully utilize the functionality of this package, you must pass a configuration array that includes all the items you wish to display in the menu. A basic example of this configuration array might look something like the following:

1import type { ConfigData } from "commandmenu";
2import type { IconName } from "components/Icon";
3
4const config: ConfigData<IconName> = [
5  {
6    id: 'github',
7    label: 'Github',
8    icon: 'Github',
9    description: 'Check github',
10    onSelect: () => console.log('github selected')
11  },
12  {
13    id: 'spotifyPlay',
14    label: 'Spotify play',
15    icon: 'Play',
16    description: 'Play songs on Spotify',
17    onSelect: () => console.log('spotify play selected')
18  },
19  {
20    id: 'spotifyNext',
21    label: 'Spotify next',
22    icon: 'Next',
23    description: 'Next song on Spotify',
24    onSelect: () => console.log('spotify next selected')
25  },
26]

Utilizing the props data returned by the hook is a straightforward process. Simply spread the `menuProps` and `searchProps`, then map through the list in order to render all the necessary menu items.

1return (
2  <CommandMenu {...menuProps}>
3    <SearchInput {...searchProps} />
4    <CommandMenuList>
5      {list.map(({ id, label, icon, description }) => {
6        const isSelected = id === selectedItem
7        return (
8          <CommandMenuListItemWrapper
9            key={id}
10            ref={isSelected ? selectedItemRef : null}
11            isSelected={isSelected}
12          >
13            {icon && <CommandMenuListItemIcon name={icon} />}
14            <CommandMenuListItemLabel>{label}</CommandMenuListItemLabel>
15            {description && (
16              <CommandMenuListItemDescription>
17                {description}
18              </CommandMenuListItemDescription>
19            )}
20          </CommandMenuListItemWrapper>
21        )
22      })}
23    </CommandMenuList>
24  </CommandMenu>
25)

Ah, you are looking for
something more advanced...

Grouping and nested menus

Groups

If you wish to group items in your menu, it's easy to do so by wrapping them in a group object configuration. Once you've done this, you're ready to go!

1{
2  id: 'favs',
3  label: 'Favorites',
4  groupItems: [
5    {
6      id: 'github',
7      label: 'Github',
8      icon: 'Github',
9      description: 'Check github',
10      onSelect: () => console.log('open github')
11    },
12    {
13      id: 'spotifyPlay',
14      label: 'Spotify play',
15      icon: 'Play',
16      description: 'Play songs on Spotify',
17      onSelect: () => console.log('spotify play')
18    },
19    {
20      id: 'spotifyNext',
21      label: 'Spotify next',
22      icon: 'Next',
23      description: 'Next song on Spotify',
24      onSelect: () => console.log('spotify next')
25    },
26  ]
27},

Once you've updated the command menu configuration, the final step is to simply render the group elements inside another list. This can be accomplished using code similar to the following:

1return (
2    <CommandMenu {...menuProps}>
3      <SearchInput {...searchProps} />
4      <CommandMenuList>
5        {list.map((item) => {
6          if (isGroupItem(item)) {
7            return (
8              <CommandMenuListGroupItem key={item.id} id="group">
9                <CommandMenuListGroupItemLabel>
10                  {item.label}
11                </CommandMenuListGroupItemLabel>
12                <CommandMenuGroupList>
13                  {item.groupItems.map((groupItem) => (
14                    <CommandMenuListItem
15                      key={groupItem.id}
16                      selectedItem={selectedItem}
17                      selectedItemRef={selectedItemRef}
18                      {...groupItem}
19                    />
20                  ))}
21                </CommandMenuGroupList>
22              </CommandMenuListGroupItem>
23            )
24          }
25          return (
26            <CommandMenuListItem
27              key={item.id}
28              selectedItem={selectedItem}
29              selectedItemRef={selectedItemRef}
30              {...item}
31            />
32          )
33        })}
34      </CommandMenuList>
35    </CommandMenu>
36  )

Nested menus

If you'd like to include multiple options related to a specific item, you can utilize nested menus. You can even add nested menus to each level, as needed. Once you've updated the configuration accordingly, this feature should work seamlessly, allowing you to take your menu functionality to the next level!

1{
2  id: 'spotify',
3  label: 'Spotify',
4  icon: 'Music',
5  description: 'Control Spotify',
6  items: [
7    {
8      id: 'spotifyPlay',
9      label: 'Play',
10      icon: 'Play',
11      onSelect: () => console.log('spotify play selected')
12    },
13    {
14      id: 'spotifyPause',
15      label: 'Pause',
16      icon: 'Pause',
17      onSelect: () => console.log('spotify pasue selected')
18    },
19    {
20      id: 'spotifyNext',
21      label: 'Next',
22      icon: 'ArrowRight',
23      onSelect: () => console.log('spotify next selected')
24    },
25    {
26      id: 'spotifyPrevious',
27      label: 'Previous',
28      icon: 'ArrowLeft',
29      onSelect: () => console.log('spotify prev selected')
30    },
31  ]
32}