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-menu
Components
<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>
)
}