Satyajeet Jadhav

a year ago

Custom Links in tiptap

Today I came across a scenario where I wanted to modify the default behavior of some <a> links in tiptap editor. 

The requirement was if the <a> link has an href attribute starting with /map clicking the link should not open the a new tab.  Opening a new tab loads the entire app again in another tab and breaks the user’s context. 

The Link extension in tiptap let’s you set target to null. However, doing that didn’t work for me. I read that browsers do not let editor links open in the same tab. Read more here Default prevented on <a> click?

So I extended the Link extension by creating the CustomLink extension. I took help from this github comment.

Since tiptap is built on top of ProseMirror API, you can use the addProseMirrorPlugins in your extensions. When a link is clicked,

  1. Identify the correct <a> tag 

    if (link && link.attributes.getNamedItem("href")?.value.startsWith("/map"))

  2. stop its propagation.

     event.preventDefault();
     event.stopPropagation();

  3. Do what needs to be done instead.

 extensionOptions.showMap(true);
 extensionOptions.setMapQuery(link.text || "");


Here is the complete code.

import Link from "@tiptap/extension-link";
import { Plugin, PluginKey } from "@tiptap/pm/state";
import { getAttributes } from "@tiptap/core";

interface CustomLinkOptions {
  showMap: (val: boolean) => void;
  setMapQuery: (val: string) => void;
}

export const CustomLink = Link.extend<CustomLinkOptions>({
  addOptions() {
    return {
      ...this.parent?.(),
      openOnClick: false,
      showMap: () => {},
      setMapQuery: () => {},
    };
  },

  addProseMirrorPlugins() {
    const plugins: Plugin[] = this.parent?.() || [];
    const extensionOptions = this.options;

    const clickHandler = new Plugin({
      key: new PluginKey("handleControlClick"),
      props: {
        handleClick(view, pos, event) {
          const attrs = getAttributes(view.state, "link");
          const link = (event.target as HTMLElement)?.closest("a");
          if (
            link &&
            link.attributes.getNamedItem("href")?.value.startsWith("/map")
          ) {
            event.preventDefault();
            event.stopPropagation();
            extensionOptions.showMap(true);
            extensionOptions.setMapQuery(link.text || "");
            return true;
          } else if (link && attrs.href) {
            window.open(attrs.href, attrs.target);
            return true;
          }
          return false;
        },
      },
    });

    plugins.push(ctrlClickHandler);

    return plugins;
  },
});

Here is how you can use the extension in Tiptap.

const editor = useEditor(
    {
      extensions: [
        CustomLink.configure({
          showMap: () => {
            showMap(true);
          },
          setMapQuery: (val: string) => {
            setMapQuery(val);
          },
        }),
       ]
   }
//..rest of your code
});


Hope this helps.

251 views

Comments ( 2 )

Matthew Bub

9 months ago

Thanks for the post, I'm building a feature that tracks links throughout an app and TipTap was the edge case. This is like the perfect template for getting started. Cheers!

Satyajeet Jadhav

9 months ago

I’m glad you found it useful! Cheers!

Participate in the conversation.

Never miss a post from
Satyajeet Jadhav

Get notified when Satyajeet Jadhav publishes a new post.

Read More

Di-Craft Studios

Issues

language pills not shifting to left side while making new entry - it cuts off the entered text and makes it hard to understand what we are writing currently.

Issues
Zaw Min Tin

Is a Sitemap.xml Really Necessary for Your Website?

A sitemap.xml is essentially a roadmap for search engines like Google and Bing. It’s a file that lists all the pages on your website that you want search engines to crawl and index. Think of it as a \"menu\" that helps search engines understand your website structure.

Is a Sitemap.xml Really Necessary for Your Website?
Satyajeet Jadhav

Semantic Search, aka Magic

The related notes feature searches all your notes to find the ones that are closest in meaning to your current note.Searching notes to find text similar in meaning to your query is called semantic search. We are trying to build a semantic search engine.

Semantic Search, aka Magic