Skip to main content
The SlashCommandMenu component provides a command palette interface that appears when users type /. It allows quick insertion of blocks by browsing or searching available plugin types.

Installation

npm install @yoopta/ui

Basic Usage

import YooptaEditor, { createYooptaEditor } from '@yoopta/editor';
import { SlashCommandMenu } from '@yoopta/ui';
import { useMemo } from 'react';

function MyEditor() {
  const editor = useMemo(() => createYooptaEditor({ plugins }), []);

  return (
    <YooptaEditor editor={editor}>
      <SlashCommandMenu>
        {({ items }) => (
          <SlashCommandMenu.Content>
            <SlashCommandMenu.List>
              <SlashCommandMenu.Empty>No blocks found</SlashCommandMenu.Empty>
              {items.map((item) => (
                <SlashCommandMenu.Item
                  key={item.id}
                  value={item.id}
                  title={item.title}
                  description={item.description}
                  icon={item.icon}
                />
              ))}
            </SlashCommandMenu.List>
            <SlashCommandMenu.Footer />
          </SlashCommandMenu.Content>
        )}
      </SlashCommandMenu>
    </YooptaEditor>
  );
}

Component API

SlashCommandMenu (Root)

The root component that manages command menu state and positioning.
children
ReactNode | ((props: SlashCommandRootChildrenProps) => ReactNode)
required
Menu content or render function receiving grouped items
items
SlashCommandItem[]
Custom items to display. If not provided, uses plugins from editor
trigger
string
default:"/"
Character that triggers the menu
onSelect
(item: SlashCommandItem) => void
Called when an item is selected
className
string
Additional CSS class name

SlashCommandMenu.Content

The floating content container.
children
ReactNode
Menu items and footer
className
string
Additional CSS class name

SlashCommandMenu.List

Scrollable list container for menu items.
children
ReactNode
Menu items
className
string
Additional CSS class name

SlashCommandMenu.Item

value
string
required
Unique identifier (typically plugin type)
title
string
required
Display title
description
string
Optional description
icon
ReactNode
Optional icon element
keywords
string[]
Additional search keywords
disabled
boolean
Whether the item is disabled
onSelect
() => void
Custom select handler

SlashCommandMenu.Group

Groups related items together.
heading
string
required
Group heading text
children
ReactNode
Menu items

SlashCommandMenu.Empty

Displayed when no items match the search.
children
ReactNode
default:"No results found"
Empty state content

SlashCommandMenu.Separator

Visual separator between items or groups. Footer section (typically shows keyboard hints).
children
ReactNode
Footer content

Examples

With Custom Icons

import { SlashCommandMenu } from '@yoopta/ui';
import { 
  Heading1, 
  List, 
  Image, 
  Code, 
  Quote 
} from 'lucide-react';

const ICON_MAP = {
  HeadingOne: Heading1,
  BulletedList: List,
  Image: Image,
  Code: Code,
  Blockquote: Quote,
};

function CustomIconMenu() {
  return (
    <SlashCommandMenu>
      {({ items }) => (
        <SlashCommandMenu.Content>
          <SlashCommandMenu.List>
            <SlashCommandMenu.Empty>No blocks found</SlashCommandMenu.Empty>
            {items.map((item) => {
              const Icon = ICON_MAP[item.id];
              return (
                <SlashCommandMenu.Item
                  key={item.id}
                  value={item.id}
                  title={item.title}
                  description={item.description}
                  icon={Icon ? <Icon size={20} /> : null}
                />
              );
            })}
          </SlashCommandMenu.List>
          <SlashCommandMenu.Footer />
        </SlashCommandMenu.Content>
      )}
    </SlashCommandMenu>
  );
}

Grouped Items

import { SlashCommandMenu } from '@yoopta/ui';

function GroupedMenu() {
  return (
    <SlashCommandMenu>
      {({ groupedItems }) => (
        <SlashCommandMenu.Content>
          <SlashCommandMenu.List>
            <SlashCommandMenu.Empty>No blocks found</SlashCommandMenu.Empty>
            {Array.from(groupedItems.entries()).map(([group, items]) => (
              <SlashCommandMenu.Group key={group} heading={group}>
                {items.map((item) => (
                  <SlashCommandMenu.Item
                    key={item.id}
                    value={item.id}
                    title={item.title}
                    description={item.description}
                    icon={item.icon}
                  />
                ))}
              </SlashCommandMenu.Group>
            ))}
          </SlashCommandMenu.List>
          <SlashCommandMenu.Footer />
        </SlashCommandMenu.Content>
      )}
    </SlashCommandMenu>
  );
}

Custom Items

import { SlashCommandMenu } from '@yoopta/ui';
import type { SlashCommandItemType } from '@yoopta/ui';
import { Zap, Star } from 'lucide-react';

const CUSTOM_ITEMS: SlashCommandItemType[] = [
  {
    id: 'ai-complete',
    title: 'AI Complete',
    description: 'Complete text with AI',
    icon: <Zap />,
    group: 'AI',
    keywords: ['ai', 'complete', 'generate'],
    onSelect: () => {
      // Custom AI completion logic
    },
  },
  {
    id: 'featured-callout',
    title: 'Featured Callout',
    description: 'Special highlighted callout',
    icon: <Star />,
    group: 'Content',
  },
];

function CustomItemsMenu() {
  return (
    <SlashCommandMenu items={CUSTOM_ITEMS}>
      {({ items }) => (
        <SlashCommandMenu.Content>
          <SlashCommandMenu.List>
            {items.map((item) => (
              <SlashCommandMenu.Item
                key={item.id}
                value={item.id}
                title={item.title}
                description={item.description}
                icon={item.icon}
              />
            ))}
          </SlashCommandMenu.List>
        </SlashCommandMenu.Content>
      )}
    </SlashCommandMenu>
  );
}
import { SlashCommandMenu } from '@yoopta/ui';

function MenuWithCustomFooter() {
  return (
    <SlashCommandMenu>
      {({ items }) => (
        <SlashCommandMenu.Content>
          <SlashCommandMenu.List>
            {items.map((item) => (
              <SlashCommandMenu.Item key={item.id} {...item} />
            ))}
          </SlashCommandMenu.List>
          <SlashCommandMenu.Footer>
            <div style={{ padding: '8px', fontSize: '12px', color: '#666' }}>
              ↑↓ Navigate • ↵ Select • Esc Close
            </div>
          </SlashCommandMenu.Footer>
        </SlashCommandMenu.Content>
      )}
    </SlashCommandMenu>
  );
}

Custom Trigger

import { SlashCommandMenu } from '@yoopta/ui';

function CustomTriggerMenu() {
  return (
    <SlashCommandMenu trigger="@">
      {/* Menu content */}
    </SlashCommandMenu>
  );
}

Behavior

Trigger Detection

The menu automatically:
  • Opens when trigger character is typed
  • Filters items based on typed text after trigger
  • Closes on escape, selection, or clicking outside
  • Handles keyboard navigation (arrow keys, enter)
Searches across:
  • Item title
  • Item description
  • Custom keywords
Fuzzy matching supported for better UX.

Keyboard Navigation

  • / - Navigate items
  • Enter - Select highlighted item
  • Escape - Close menu
  • Type to filter

Selection Behavior

When an item is selected:
  1. Trigger character and search text are removed
  2. New block is inserted at current position
  3. Focus moves to the new block
  4. Menu closes

Item Type

type SlashCommandItem = {
  id: string;              // Unique identifier (plugin type)
  title: string;           // Display title
  description?: string;    // Optional description
  icon?: ReactNode;        // Optional icon
  keywords?: string[];     // Search keywords
  group?: string;          // Group name for grouping
  disabled?: boolean;      // Disable selection
  onSelect?: () => void;   // Custom select handler
};

Styling

CSS Classes

.yoopta-ui-slash-command-root {
  /* Menu container */
}

.yoopta-ui-slash-command-content {
  /* Content wrapper */
}

.yoopta-ui-slash-command-list {
  /* Scrollable list */
}

.yoopta-ui-slash-command-item {
  /* Menu item */
}

.yoopta-ui-slash-command-item[data-selected="true"] {
  /* Selected item */
}

.yoopta-ui-slash-command-group {
  /* Group container */
}

.yoopta-ui-slash-command-footer {
  /* Footer section */
}

Custom Styling

<SlashCommandMenu className="my-menu">
  {({ items }) => (
    <SlashCommandMenu.Content className="my-content">
      <SlashCommandMenu.List className="my-list">
        {items.map((item) => (
          <SlashCommandMenu.Item
            key={item.id}
            {...item}
            className="my-item"
          />
        ))}
      </SlashCommandMenu.List>
    </SlashCommandMenu.Content>
  )}
</SlashCommandMenu>

TypeScript

import type {
  SlashCommandItemType,
  SlashCommandGroupType,
  SlashCommandRootProps,
  SlashCommandContentProps,
  SlashCommandListProps,
  SlashCommandItemProps,
  SlashCommandGroupProps,
} from '@yoopta/ui';

See Also

Build docs developers (and LLMs) love