WordPress has a quick way to link to your other notes without copy-pasting. If you type [[
(just as in in Roam Research), you will invoke a dropdown to search your posts and pages. Like so:

This is called an “autocompleter”. It’s the same dropdown that powers block inserter (invoked by /
) and the people-tagger (invoked by @
).
You can see the source of the links completer here.
How to add your post type to autocompleter
I am currently writing a WordPress-based notetaking system that aims to simplify your entire knowledge management stack, from reading to publishing, all inside WordPress.
As part of this endeavour, I am introducing a Note
post type that will never get published, but can be embedded and linked from everywhere and to everywhere.
And I want this “Note” post type to show in the completer alongsited Posts and Pages whenever you type [[
. There are 2 problems with this, however:
- As you can see in the above source, the completer uses the `/wp/v2/search’` endpoint, which only searches public-facing content of the site, so it will not work for my Notes post type.
- The completer for the
[[
command already exists and there is no way to augment that specific one. So what should we do?
Merging completers
What if we had a third completer that would merge the result of 2 others and serve as a “unified” completer?
Let’s do that! First, we need a completer for our Note post type. This completer will insert a Note block instead of linking to the note, but it could link by returning <a>
in getOptionCompletion
.
const NoteCompleter = {
name: 'links',
className: 'block-editor-autocompleters__link',
triggerPrefix: '[[',
options: async ( letters ) => {
let options = await apiFetch( {
path: addQueryArgs( '/pos/v1/notes', {
per_page: 10,
search: letters,
} ),
} );
options = options.map( ( { id, title, type, excerpt } ) => ( {
id,
title: title.rendered,
type,
excerpt: excerpt.rendered.replace( /(<([^>]+)>)/gi, '' ).substring( 0, 100 ),
} ) );
return options;
},
getOptionKeywords( item ) {
const expansionWords = item.title.split( /\s+/ );
const experptWords = item.excerpt.split( /\s+/ );
return [ ...expansionWords, ...experptWords ];
},
getOptionLabel( item ) {
return (
<>
<Icon
key="icon"
icon={ overlayText }
/>
{ item.title || item.excerpt }
</>
);
},
getOptionCompletion( item ) {
return {
action: 'replace',
value: createBlock( 'pos/note', {
note_id: item.id,
} ),
}
},
}
In order to merge this completer with the links
from core, we will need this mergeCompleters function:
- It assumes name, classname and triggerPrefix are similar for all these completers
- It will trigger `options` methods in parallel and merge their results
- Each result will have a
completer
index attached, so that the expansion gets handled by appropriategetOptionKeywords
,getOptionLabel
andgetOptionCompletion
from the original completers
function mergeCompleters( completers ) {
return {
name: completers[0].name,
className: completers[0].className,
triggerPrefix: completers[0].triggerPrefix,
options: async ( letters ) => {
const completerResults = await Promise.all(
completers.map( completer => completer.options( letters ) )
);
const opt = completerResults.map( ( completer, completerId ) => completer.map( option => ( { ...option, completer: completerId } ) ) ).flat();
return opt;
},
getOptionKeywords: ( item ) => completers[item.completer].getOptionKeywords( item ),
getOptionLabel: ( item ) => completers[item.completer].getOptionLabel( item ),
getOptionCompletion: ( item ) => completers[item.completer].getOptionCompletion( item ),
}
}
And now we hook into editor.Autocomplete.completers
- We filter out original links completer
- Merge it with our NotesCompleter
- And feed back to to the completer list
function appendMergedCompleter( completers, blockName ) {
const linksCompleter = completers.find( ( { name } ) => name === 'links' );
const allCompleters = completers.filter( ( { name } ) => name !== 'links' );
return [ mergeCompleters( [ linksCompleter, NoteCompleter ] ), ...allCompleters ]
}
// Adding the filter
wp.hooks.addFilter(
'editor.Autocomplete.completers',
'pos/autocompleters/links-and-notes',
appendMergedCompleter
);
And now we have the Notes post type alongside regular results:

The post Augmenting WordPress autocompleter quick links with your own post type appeared first on Artur Piszek.