Inline ghost text (autocomplete suggestions) plugin for Lexical.
Renders translucent suggestion text at the cursor position inside a Lexical editor. Tab accepts, Escape dismisses.
# pnpm
pnpm add @scrivenmark/lexical-ghost-text
# npm
npm install @scrivenmark/lexical-ghost-text
# yarn
yarn add @scrivenmark/lexical-ghost-textimport { useRef } from 'react'
import { LexicalComposer } from '@lexical/react/LexicalComposer'
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'
import { ContentEditable } from '@lexical/react/LexicalContentEditable'
import {
GhostTextNode,
GhostTextPlugin,
GhostTextHandle,
} from '@scrivenmark/lexical-ghost-text'
import '@scrivenmark/lexical-ghost-text/styles.css'
const initialConfig = {
namespace: 'MyEditor',
nodes: [GhostTextNode],
onError: (err: Error) => { throw err },
}
export function Editor() {
const ghostRef = useRef<GhostTextHandle>(null)
function handleKeyUp() {
// Supply a suggestion however you like
ghostRef.current?.showSuggestion('world')
}
return (
<LexicalComposer initialConfig={initialConfig}>
<RichTextPlugin
contentEditable={<ContentEditable onKeyUp={handleKeyUp} />}
placeholder={<div>Start typing…</div>}
ErrorBoundary={LexicalErrorBoundary}
/>
<GhostTextPlugin
onReady={(handle) => { ghostRef.current = handle }}
/>
</LexicalComposer>
)
}Extends DecoratorNode. Inline, non-selectable. Renders with
aria-hidden="true" so screen readers ignore the suggestion.
Apply custom styles via config.theme.ghostText (defaults to the
class lexical-ghost-text).
Factory function. Creates a GhostTextNode with the given text.
Type guard.
Serialisation type for GhostTextNode.
React component. Must be placed inside a LexicalComposer.
| Prop | Type | Description |
|---|---|---|
onReady |
(handle: GhostTextHandle) => void |
Optional. Called once the plugin is mounted, providing the imperative handle. |
Imperative handle returned via onReady.
| Method | Signature | Description |
|---|---|---|
showSuggestion |
(text: string) => void |
Insert or replace the ghost text at the cursor. |
dismissSuggestion |
() => void |
Remove the ghost text node if present. |
hasSuggestion |
() => boolean |
Returns true if a ghost text node is currently active. |
interface GhostTextPluginProps {
onReady?: (handle: GhostTextHandle) => void
}Import the default stylesheet:
import '@scrivenmark/lexical-ghost-text/styles.css'Or target the CSS class yourself (set via theme.ghostText in your
editor config, defaults to lexical-ghost-text):
.lexical-ghost-text {
color: #9ca3af;
pointer-events: none;
user-select: none;
}| Key | Action |
|---|---|
Tab |
Accept suggestion — replaces ghost text with real text |
Escape |
Dismiss suggestion |
| Any other key | Dismiss suggestion |
| Package | Version |
|---|---|
lexical |
^0.40.0 |
@lexical/react |
^0.40.0 |
react |
^18.0.0 || ^19.0.0 |
See CONTRIBUTING.md.
MIT © Lettergraph