# How to Customize the Slash Command Menu with New Commands in reactjs-tiptap-editor

> Learn how to customize the slash command menu in reactjs-tiptap-editor by adding new commands. Extend the default functionality and enhance your editor experience effortlessly.

- Repository: [Hung Hoang/reactjs-tiptap-editor](https://github.com/hunghg255/reactjs-tiptap-editor)
- Tags: how-to-guide
- Published: 2026-03-03

---

**You can customize the slash command menu by providing a custom `CommandList` array to the `<SlashCommandList />` component, which replaces or extends the default groups stored in the reactive signal at [`src/store/commandList.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/store/commandList.ts).**

The **Slash Command** extension in `hunghg255/reactjs-tiptap-editor` implements a suggestion-based popup that appears when users type "/". By understanding how the command list flows from the signal store to the UI components, you can inject bespoke formatting options, custom embeds, or entirely new command groups tailored to your application’s workflow.

## Understanding the Slash Command Architecture

The extension follows a reactive pattern where the command list lives in a global signal and gets filtered at runtime based on the user’s query.

### The Command List Structure

The type definitions in [`src/extensions/SlashCommand/types.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/SlashCommand/types.ts) establish the contract for all slash menu items. A **CommandList** is an array of groups, where each group contains a `name`, `title`, and an array of **Command** objects. Each command requires:

- `name`: Unique identifier
- `label`: User-facing text
- `aliases`: Optional array for alternative search terms
- `iconName`: Icon identifier for the UI
- `action`: Callback receiving the editor instance and matched range
- Optional visibility logic via `shouldBeHidden` and `isActive` callbacks

### The Reactive Signal Store

Instead of prop-drilling, the extension uses [`src/store/commandList.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/store/commandList.ts) to hold the current list in a reactive signal accessed via `useSignalCommandList`. The `SlashCommandList` component ([`src/extensions/SlashCommand/components/SlashCommandList.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/SlashCommand/components/SlashCommandList.tsx)) acts as the entry point: if you omit the `commandList` prop, it calls `renderCommandListDefault({ t })` from [`src/extensions/SlashCommand/renderCommandListDefault.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/SlashCommand/renderCommandListDefault.ts) and pushes the result into the signal. If you provide a custom list, it stores yours instead, giving you full control over the menu contents.

## Creating Custom Slash Commands

To add new commands, define an array matching the `CommandList` interface with your custom groups and actions.

First, create a new file (e.g., [`src/custom/mySlashCommands.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/custom/mySlashCommands.ts)) and import the necessary types:

```typescript
import { CommandList } from '@/extensions/SlashCommand/types';
import { t } from '@/locales';

export const mySlashCommands: CommandList[] = [
  {
    name: 'format',
    title: t('editor.slash.format'),
    commands: [
      {
        name: 'highlight',
        label: 'Highlight',
        aliases: ['hl'],
        iconName: 'Highlighter',
        shouldBeHidden: (editor) => editor.isActive('codeBlock'),
        isActive: (editor) => editor.isActive('highlight'),
        action: ({ editor, range }) => {
          editor.chain().focus().deleteRange(range).run();
          editor.chain().toggleHighlight().run();
        },
      },
    ],
  },
  {
    name: 'insert',
    title: t('editor.slash.insert'),
    commands: [
      {
        name: 'blockquote',
        label: t('editor.blockquote.tooltip'),
        iconName: 'TextQuote',
        action: ({ editor, range }) => {
          editor.chain().focus().deleteRange(range).setBlockquote().run();
        },
      },
    ],
  },
];

```

Key implementation details:
- **Delete the range first**: Always call `deleteRange(range)` in your action to remove the typed "/" character before executing the command.
- **Conditional visibility**: Use `shouldBeHidden` to hide commands in incompatible contexts (e.g., disabling a highlight command inside code blocks).
- **Active state**: Implement `isActive` to provide visual feedback when the cursor is inside content where the command has already been applied.

## Integrating Custom Commands into Your Editor

Pass your custom array to the `SlashCommandList` component inside your editor UI. Ensure you import `SlashCommand` from the slashcommand sub-package to keep the suggestion plugin active.

```tsx
import { RichTextProvider, useEditor, EditorContent } from 'reactjs-tiptap-editor';
import { SlashCommand, SlashCommandList } from 'reactjs-tiptap-editor/slashcommand';
import { mySlashCommands } from '@/custom/mySlashCommands';

export const App = () => {
  const editor = useEditor({
    extensions: [
      // ...other extensions
      SlashCommand,
    ],
  });

  return (
    <RichTextProvider editor={editor}>
      <SlashCommandList commandList={mySlashCommands} />
      <EditorContent editor={editor} />
    </RichTextProvider>
  );
};

```

When the component mounts, it updates the signal in [`src/store/commandList.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/store/commandList.ts) with your custom data. The `SlashCommandNodeView` component ([`src/extensions/SlashCommand/components/SlashCommandNodeView.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/SlashCommand/components/SlashCommandNodeView.tsx)) then reads this signal to render the floating menu, while `useFilterCommandList` handles query filtering as the user types.

## Replacing vs. Extending the Default List

**Complete replacement**: If you want to hide all default commands (headings, lists, embeds, etc.), simply provide a full `CommandList` array covering all your desired functionality without importing `renderCommandListDefault`.

**Extension**: To keep the defaults and add your own, import the default renderer, spread its result, and append your custom groups:

```typescript
import { renderCommandListDefault } from '@/extensions/SlashCommand/renderCommandListDefault';

const customList = [
  ...renderCommandListDefault({ t }),
  {
    name: 'custom',
    title: 'Custom Tools',
    commands: [ /* your commands */ ],
  },
];

```

## Summary

- The slash command menu is driven by a reactive signal in [`src/store/commandList.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/store/commandList.ts) that stores an array of `CommandList` groups.
- You customize the menu by passing a `commandList` prop to `<SlashCommandList />`, which overrides the default call to `renderCommandListDefault`.
- Each command object requires an `action` callback that receives the TipTap editor instance and the matched text range.
- Use `shouldBeHidden` and `isActive` callbacks to control command visibility and highlights based on editor state.
- The filtering logic in [`renderCommandListDefault.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/renderCommandListDefault.ts) automatically handles search queries against command names, labels, and aliases.

## Frequently Asked Questions

### How do I hide specific default commands without rewriting the entire list?

You cannot selectively hide individual default commands through props. Instead, import `renderCommandListDefault`, then filter or map over the returned array to remove unwanted items before passing the modified array to `SlashCommandList`.

### Can I add async actions or API calls inside a slash command?

Yes. The `action` callback receives the editor instance and can execute any asynchronous logic. Simply ensure you handle the `range` deletion synchronously first, then perform your async operation and subsequent editor updates.

### Why is my custom command still showing inside a code block?

Verify that your `shouldBeHidden` callback correctly checks the editor state. The function receives the editor instance; return `true` to hide the command. For example: `shouldBeHidden: (editor) => editor.isActive('codeBlock')` prevents the command from appearing inside code blocks.

### Where is the slash command suggestion plugin actually registered?

The suggestion logic is registered in [`src/extensions/SlashCommand/SlashCommand.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/SlashCommand/SlashCommand.ts), which configures the TipTap suggestion plugin to trigger on the "/" character and coordinates with the `SlashCommandNodeView` component to position the popup.