The search extension provides powerful search and replace capabilities with support for regex, case sensitivity, and whole word matching.
Basic Usage
import { defineSearchQuery , defineSearchCommands } from '@prosekit/extensions/search'
import { union } from '@prosekit/core'
const extension = union ([
defineSearchQuery ({
search: 'hello' ,
}),
defineSearchCommands (),
])
Search Query
Define the search query and options:
import { defineSearchQuery } from '@prosekit/extensions/search'
const extension = defineSearchQuery ({
search: 'ProseKit' ,
replace: 'Editor' ,
caseSensitive: false ,
regexp: false ,
wholeWord: false ,
})
API Reference
The search string or regular expression pattern.
The replacement text for replace operations.
Whether the search is case-sensitive.
When true, disables automatic conversion of \n, \r, and \t in the search string.
When true, interprets the search string as a regular expression.
Enable whole-word matching (only match complete words).
Commands
The search extension provides several commands for navigation and replacement:
interface SearchCommands {
findNext : () => Command
findPrev : () => Command
findNextNoWrap : () => Command
findPrevNoWrap : () => Command
replaceNext : () => Command
replaceNextNoWrap : () => Command
replaceCurrent : () => Command
replaceAll : () => Command
}
Command Descriptions
Jump to the next match, wrapping to the beginning if at the end.
Jump to the previous match, wrapping to the end if at the beginning.
Jump to the next match without wrapping.
Jump to the previous match without wrapping.
Replace the current match and jump to the next one (with wrapping).
Replace the current match and jump to the next one (without wrapping).
Replace only the current match without moving to the next one.
Replace all matches in the document at once.
Complete Example
import { createEditor } from '@prosekit/core'
import { defineBasicExtension } from '@prosekit/extensions/basic'
import { defineSearchQuery , defineSearchCommands } from '@prosekit/extensions/search'
import { union } from '@prosekit/core'
const editor = createEditor ({
extension: union ([
defineBasicExtension (),
defineSearchQuery ({
search: 'example' ,
replace: 'sample' ,
}),
defineSearchCommands (),
]),
})
// Navigate through matches
editor . commands . findNext ()
editor . commands . findPrev ()
// Replace operations
editor . commands . replaceCurrent ()
editor . commands . replaceAll ()
React Search UI
import { useEditor } from '@prosekit/react'
import { useState } from 'react'
function SearchPanel () {
const editor = useEditor ()
const [ searchText , setSearchText ] = useState ( '' )
const [ replaceText , setReplaceText ] = useState ( '' )
const [ caseSensitive , setCaseSensitive ] = useState ( false )
const [ useRegex , setUseRegex ] = useState ( false )
return (
< div className = "search-panel" >
< div className = "search-inputs" >
< input
type = "text"
placeholder = "Search..."
value = { searchText }
onChange = {(e) => setSearchText (e.target.value)}
/>
< input
type = "text"
placeholder = "Replace..."
value = { replaceText }
onChange = {(e) => setReplaceText (e.target.value)}
/>
</ div >
< div className = "search-options" >
< label >
< input
type = "checkbox"
checked = { caseSensitive }
onChange = {(e) => setCaseSensitive (e.target.checked)}
/>
Case sensitive
</ label >
< label >
< input
type = "checkbox"
checked = { useRegex }
onChange = {(e) => setUseRegex (e.target.checked)}
/>
Use regex
</ label >
</ div >
< div className = "search-actions" >
< button onClick = {() => editor.commands.findPrev()} >
Previous
</ button >
< button onClick = {() => editor.commands.findNext()} >
Next
</ button >
< button onClick = {() => editor.commands.replaceCurrent()} >
Replace
</ button >
< button onClick = {() => editor.commands.replaceAll()} >
Replace All
</ button >
</ div >
</ div >
)
}
Styling Search Matches
The extension adds CSS classes to search matches:
/* All search matches */
.ProseMirror-search-match {
background-color : #fef3c7 ;
border-radius : 2 px ;
}
/* The currently active match */
.ProseMirror-active-search-match {
background-color : #fbbf24 ;
outline : 2 px solid #f59e0b ;
}
/* Dark mode */
.dark .ProseMirror-search-match {
background-color : #854d0e ;
}
.dark .ProseMirror-active-search-match {
background-color : #a16207 ;
outline-color : #ca8a04 ;
}
Advanced Examples
Regex Search
import { defineSearchQuery } from '@prosekit/extensions/search'
const extension = defineSearchQuery ({
search: 'https?://[^ \\ s]+' ,
regexp: true ,
})
// Find all URLs in the document
editor . commands . findNext ()
Whole Word Search
import { defineSearchQuery } from '@prosekit/extensions/search'
const extension = defineSearchQuery ({
search: 'cat' ,
wholeWord: true ,
})
// Matches "cat" but not "category" or "concatenate"
Case-Sensitive Search
import { defineSearchQuery } from '@prosekit/extensions/search'
const extension = defineSearchQuery ({
search: 'ProseKit' ,
caseSensitive: true ,
})
// Matches "ProseKit" but not "prosekit" or "PROSEKIT"
Dynamic Search
Update the search query dynamically:
import { createEditor } from '@prosekit/core'
import { SearchQuery } from 'prosemirror-search'
function updateSearch ( editor : Editor , searchText : string ) {
// Get the search plugin
const searchPlugin = editor . view . state . plugins . find (
( p ) => p . key === 'prosemirror-search'
)
if ( searchPlugin ) {
const query = new SearchQuery ({
search: searchText ,
caseSensitive: false ,
regexp: false ,
})
// Update the search
editor . view . dispatch (
editor . view . state . tr . setMeta ( searchPlugin , { query })
)
}
}
Use Cases
Find & Replace Enable users to find and replace text throughout their documents.
Content Review Help users locate specific terms or phrases during content review.
Refactoring Support bulk renaming and refactoring in code editors.
Translation Find and replace terms during localization and translation workflows.
Keyboard Shortcuts
Consider adding keyboard shortcuts for common search operations:
import { defineKeymap } from '@prosekit/core'
const searchKeymap = defineKeymap ({
'Mod-f' : () => {
// Open search panel
return true
},
'Mod-g' : findNext ,
'Mod-Shift-g' : findPrev ,
'Mod-h' : () => {
// Open replace panel
return true
},
'Escape' : () => {
// Close search panel
return true
},
})
Tips
The search automatically scrolls the active match into view, ensuring users can always see the current result.
Use findNextNoWrap and findPrevNoWrap when you want to prevent wrapping to the beginning/end of the document.
Be careful with replaceAll on large documents with many matches. Consider showing a confirmation dialog first.
Search decorations are updated automatically as the document changes. Matches remain highlighted even as users edit.