AI Menu
A fully-featured AI-powered contextual menu for Tiptap editors. Provides intelligent content editing, generation, and transformation capabilities with floating menu positioning and customizable AI actions.
Installation
Add the component via the Tiptap CLI:
npx @tiptap/cli@latest add ai-menuComponents
<AiMenu />
A comprehensive AI-powered menu that provides contextual editing and generation capabilities.
Usage
import { EditorContent, EditorContext, useEditor } from '@tiptap/react' // --- Tiptap Core Extensions --- import { StarterKit } from '@tiptap/starter-kit' import { Ai } from '@tiptap-pro/extension-ai' import { UiState } from '@/components/tiptap-extension/ui-state-extension' import { HorizontalRule } from '@/components/tiptap-node/horizontal-rule-node/horizontal-rule-node-extension' import { Selection } from '@tiptap/extensions' import { AiProvider, useAi } from '@/components/contexts/ai-context' // --- Tiptap UI --- import { AiMenu } from '@/components/tiptap-ui/ai-menu' import { AiAskButton } from '@/components/tiptap-ui/ai-ask-button' // --- UI Primitive --- import { ButtonGroup } from '@/components/tiptap-ui-primitive/button' // --- Utils --- import { TIPTAP_AI_APP_ID } from '@/lib/tiptap-collab-utils' // --- Tiptap Node --- import '@/components/tiptap-node/blockquote-node/blockquote-node.scss' import '@/components/tiptap-node/code-block-node/code-block-node.scss' import '@/components/tiptap-node/horizontal-rule-node/horizontal-rule-node.scss' import '@/components/tiptap-node/heading-node/heading-node.scss' import '@/components/tiptap-node/paragraph-node/paragraph-node.scss' export const AiMenuExample = () => { return ( <AiProvider> <AiEditorWrapper /> </AiProvider> ) } const AiEditorWrapper = () => { const { aiToken } = useAi() if (!aiToken) { return <div className="tiptap-editor-wrapper">Loading AI...</div> } return <AiEditor aiToken={aiToken} /> } const AiEditor = ({ aiToken }: { aiToken: string }) => { const editor = useEditor({ immediatelyRender: false, extensions: [ StarterKit.configure({ horizontalRule: false, }), HorizontalRule, Selection, UiState, Ai.configure({ appId: TIPTAP_AI_APP_ID, token: aiToken, autocompletion: false, showDecorations: true, hideDecorationsOnStreamEnd: false, onLoading: (context) => { context.editor.commands.aiGenerationSetIsLoading(true) context.editor.commands.aiGenerationHasMessage(false) }, onChunk: (context) => { context.editor.commands.aiGenerationSetIsLoading(true) context.editor.commands.aiGenerationHasMessage(true) }, onSuccess: (context) => { const hasMessage = !!context.response context.editor.commands.aiGenerationSetIsLoading(false) context.editor.commands.aiGenerationHasMessage(hasMessage) }, }), ], content: ` <p>Today, we're exploring how AI is transforming creative workflows. From writing assistance to intelligent summarization, the tools at our fingertips are evolving fast. But how do we use them responsibly?</p> <p>In this article, we’ll look at real-world examples of AI enhancing—not replacing—human creativity.</p> `, }) return ( <EditorContext.Provider value={{ editor }}> <div className="controls-bar"> <div className="control-item"> <ButtonGroup orientation="horizontal"> <AiAskButton /> </ButtonGroup> </div> </div> <EditorContent editor={editor} role="presentation" className="control-showcase"> <AiMenu /> </EditorContent> </EditorContext.Provider> ) }Props
| Name | Type | Default | Description |
|---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance |
<AiMenuStateProvider />
State provider that manages AI menu state across the application.
Usage
import { AiMenuStateProvider } from '@/components/tiptap-ui/ai-menu' function App() { return <AiMenuStateProvider>{/* Your editor components */}</AiMenuStateProvider> }<AiMenuContent />
The main content component that renders the AI menu interface.
Props
| Name | Type | Default | Description |
|---|---|---|---|
editor | Editor | null | undefined | The Tiptap editor instance |
Hooks
useAiMenuState()
Hook to access and manage AI menu state.
Usage
import { useAiMenuState } from '@/components/tiptap-ui/ai-menu' function MyComponent() { const { state, updateState, setFallbackAnchor, reset } = useAiMenuState() const handleOpenMenu = () => { updateState({ isOpen: true, shouldShowInput: true }) } const handleCloseMenu = () => { reset() } return ( <button onClick={state.isOpen ? handleCloseMenu : handleOpenMenu}> {state.isOpen ? 'Close AI Menu' : 'Open AI Menu'} </button> ) }Return Values
| Name | Type | Description |
|---|---|---|
state | AiMenuState | Current AI menu state |
updateState | (updates: Partial<AiMenuState>) => void | Function to update menu state |
setFallbackAnchor | (element: HTMLElement | null, rect?) => void | Set fallback positioning anchor |
reset | () => void | Reset menu state to initial state |
useAiContentTracker()
Hook to track AI-generated content changes in the editor.
Usage
import { useAiContentTracker } from '@/components/tiptap-ui/ai-menu' function MyComponent() { const { editor } = useTiptapEditor() useAiContentTracker(editor, { onContentChange: (hasAiContent) => { console.log('AI content detected:', hasAiContent) }, }) return <div>Component with AI content tracking</div> }useTextSelectionTracker()
Hook to track text selection changes for menu positioning.
Usage
import { useTextSelectionTracker } from '@/components/tiptap-ui/ai-menu' function MyComponent() { const { editor } = useTiptapEditor() useTextSelectionTracker(editor, { onSelectionChange: (selection) => { console.log('Selection changed:', selection) }, }) return <div>Component with selection tracking</div> }Sub-Components
<AiMenuItems />
Component that renders the available AI actions grouped by category.
Usage
import { AiMenuItems } from '@/components/tiptap-ui/ai-menu' function CustomAiMenu() { const { editor } = useTiptapEditor() return ( <AiMenuItems editor={editor} availableActions={['improveWriting', 'aiFixSpellingAndGrammar', 'summarize']} /> ) }<AiMenuActions />
Component that renders action buttons for the AI menu (Accept, Regenerate, etc.).
Usage
import { AiMenuActions } from '@/components/tiptap-ui/ai-menu' function CustomAiMenu() { const { editor } = useTiptapEditor() return ( <AiMenuActions editor={editor} onAccept={() => console.log('AI content accepted')} onRegenerate={() => console.log('Regenerating AI content')} /> ) }<AiMenuInputTextarea />
Input component for custom AI prompts.
Usage
import { AiMenuInputTextarea } from '@/components/tiptap-ui/ai-menu' function CustomPromptInput() { const handleSubmit = (prompt: string) => { console.log('User prompt:', prompt) } return ( <AiMenuInputTextarea onSubmit={handleSubmit} placeholder="Ask AI to help with your content..." /> ) }Utilities
getContextAndInsertAt(editor)
Utility function to determine the context and insertion point for AI operations.
import { getContextAndInsertAt } from '@/components/tiptap-ui/ai-menu' const { context, insertAt, isSelection } = getContextAndInsertAt(editor) if (isSelection) { // Handle selection-based AI operation editor.chain().aiEdit({ prompt: 'Improve this text', insertAt }).run() } else { // Handle insertion-based AI operation editor.chain().aiGenerate({ prompt: 'Write about AI', insertAt }).run() }findPrioritizedAIElement(editor)
Finds the most appropriate DOM element for AI menu positioning.
import { findPrioritizedAIElement } from '@/components/tiptap-ui/ai-menu' const targetElement = findPrioritizedAIElement(editor) if (targetElement) { // Position menu relative to this element }AI Actions
The AI Menu provides several built-in actions organized by category:
Edit Actions
- Adjust Tone: Change the tone of selected text
- Fix Spelling & Grammar: Correct spelling and grammar errors
- Extend: Expand on the selected content
- Shorten: Make the content more concise
- Simplify Language: Use simpler, clearer language
- Improve Writing: Enhance overall writing quality
- Emojify: Add relevant emojis to the content
Write Actions
- Continue Writing: Generate continuation of the content
- Summarize: Create a summary of the selected text
- Translate To: Translate content to different languages
State Management
AiMenuState Interface
interface AiMenuState { isOpen: boolean tone?: string language: string shouldShowInput: boolean inputIsFocused: boolean fallbackAnchor: { element: HTMLElement | null rect: DOMRect | null } }State Updates
The AI menu state can be updated using the updateState function:
const { updateState } = useAiMenuState() // Open menu with input focused updateState({ isOpen: true, shouldShowInput: true, inputIsFocused: true, }) // Set language for translation updateState({ language: 'es' }) // Set tone for content adjustment updateState({ tone: 'professional' })Requirements
Dependencies
@tiptap/react- Core Tiptap React integration@tiptap-pro/extension-ai- AI extension for content generation@tiptap/starter-kit- Basic Tiptap extensionsreact-hotkeys-hook- Keyboard shortcut management
Extensions
ui-state-extension- Manages UI state for AI operationsselection-extension- Enhanced text selection handling
Referenced Components
use-tiptap-editor(hook)use-ui-editor-state(hook)menu(primitive)button,button-group(primitive)card(primitive)combobox(primitive)tiptap-utils(lib)sparkles-icon,stop-circle-2-icon(icons)
Configuration
AI Provider Setup
import { AiProvider } from '@/contexts/ai-context' function App() { return ( <AiProvider appId="your-app-id" token="your-ai-token"> <YourEditor /> </AiProvider> ) }