From db2c4e6b2e6470a54a890da1f1cfb282ad6acc7e Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Tue, 21 Apr 2026 12:19:16 +0200 Subject: [PATCH] Added link customization options to editor --- .../docs/features/blocks/inline-content.mdx | 47 +++++++++++++++++++ packages/core/src/editor/BlockNoteEditor.ts | 19 ++++++++ .../managers/ExtensionManager/extensions.ts | 5 +- .../Link/helpers/clickHandler.ts | 6 +++ .../extensions/tiptap-extensions/Link/link.ts | 20 ++++++-- 5 files changed, 92 insertions(+), 5 deletions(-) diff --git a/docs/content/docs/features/blocks/inline-content.mdx b/docs/content/docs/features/blocks/inline-content.mdx index ca7799d841..15a3233d86 100644 --- a/docs/content/docs/features/blocks/inline-content.mdx +++ b/docs/content/docs/features/blocks/inline-content.mdx @@ -79,6 +79,53 @@ type Link = { }; ``` +### Customizing Links + +You can customize how links are rendered and how they respond to clicks with the `links` editor option. + +```ts +const editor = BlockNoteEditor.create({ + links: { + HTMLAttributes: { + class: "my-link-class", + target: "_blank", + }, + onClick: (event) => { + // Custom click logic, e.g. routing without a page reload. + }, + }, +}); +``` + +#### `HTMLAttributes` + +Additional HTML attributes that should be added to rendered link elements. + +```ts +const editor = BlockNoteEditor.create({ + links: { + HTMLAttributes: { + class: "my-link-class", + target: "_blank", + }, + }, +}); +``` + +#### `onClick` + +Custom handler invoked when a link is clicked. If left `undefined`, links are opened in a new window on click (the default behavior). If provided, that default behavior is disabled and this function is called instead. + +```ts +const editor = BlockNoteEditor.create({ + links: { + onClick: (event) => { + // Do something when a link is clicked. + }, + }, +}); +``` + ## Default Styles The default text formatting options in BlockNote are represented by the `Styles` in the default schema: diff --git a/packages/core/src/editor/BlockNoteEditor.ts b/packages/core/src/editor/BlockNoteEditor.ts index 61a4ae3c6c..0378af6ad3 100644 --- a/packages/core/src/editor/BlockNoteEditor.ts +++ b/packages/core/src/editor/BlockNoteEditor.ts @@ -140,6 +140,25 @@ export interface BlockNoteEditorOptions< NoInfer >[]; + /** + * Options for configuring how links behave in the editor. + */ + links?: { + /** + * HTML attributes to add to rendered link elements. + * + * @default {} + * @example { class: "my-link-class", target: "_blank" } + */ + HTMLAttributes?: Record; + /** + * Custom handler invoked when a link is clicked. If left `undefined`, + * links are opened in a new window on click. If provided, the default + * open-on-click behavior is disabled and this function is called instead. + */ + onClick?: (event: MouseEvent) => void; + }; + /** * @deprecated, provide placeholders via dictionary instead * @internal diff --git a/packages/core/src/editor/managers/ExtensionManager/extensions.ts b/packages/core/src/editor/managers/ExtensionManager/extensions.ts index 487063b861..6e77780f22 100644 --- a/packages/core/src/editor/managers/ExtensionManager/extensions.ts +++ b/packages/core/src/editor/managers/ExtensionManager/extensions.ts @@ -73,7 +73,10 @@ export function getDefaultTiptapExtensions( SuggestionAddMark, SuggestionDeleteMark, SuggestionModificationMark, - Link, + Link.configure({ + HTMLAttributes: options.links?.HTMLAttributes ?? {}, + onClick: options.links?.onClick, + }), ...(Object.values(editor.schema.styleSpecs).map((styleSpec) => { return styleSpec.implementation.mark.configure({ editor: editor, diff --git a/packages/core/src/extensions/tiptap-extensions/Link/helpers/clickHandler.ts b/packages/core/src/extensions/tiptap-extensions/Link/helpers/clickHandler.ts index fe31821e77..e8fb399ce0 100644 --- a/packages/core/src/extensions/tiptap-extensions/Link/helpers/clickHandler.ts +++ b/packages/core/src/extensions/tiptap-extensions/Link/helpers/clickHandler.ts @@ -6,6 +6,7 @@ import { Plugin, PluginKey } from "@tiptap/pm/state"; type ClickHandlerOptions = { type: MarkType; editor: Editor; + onClick?: (event: MouseEvent) => void; }; export function clickHandler(options: ClickHandlerOptions): Plugin { @@ -46,6 +47,11 @@ export function clickHandler(options: ClickHandlerOptions): Plugin { return false; } + if (options.onClick) { + options.onClick(event); + return true; + } + const attrs = getAttributes(view.state, options.type.name); const href = link.href ?? attrs.href; const target = link.target ?? attrs.target; diff --git a/packages/core/src/extensions/tiptap-extensions/Link/link.ts b/packages/core/src/extensions/tiptap-extensions/Link/link.ts index c0c1811d4f..a95a2069d6 100644 --- a/packages/core/src/extensions/tiptap-extensions/Link/link.ts +++ b/packages/core/src/extensions/tiptap-extensions/Link/link.ts @@ -55,10 +55,15 @@ function shouldAutoLink(url: string): boolean { return true; } +export type LinkOptions = { + HTMLAttributes: Record; + onClick?: (event: MouseEvent) => void; +}; + /** * BlockNote Link mark extension. */ -export const Link = Mark.create({ +export const Link = Mark.create({ name: "link", priority: 1000, @@ -69,6 +74,13 @@ export const Link = Mark.create({ inclusive: false, + addOptions() { + return { + HTMLAttributes: {}, + onClick: undefined, + }; + }, + addAttributes() { return { href: { @@ -156,14 +168,14 @@ export const Link = Mark.create({ defaultProtocol: DEFAULT_PROTOCOL, validate: isAllowedUri, shouldAutoLink, - }) + }), ); plugins.push( clickHandler({ type: this.type, editor: this.editor, - }) + }), ); plugins.push( @@ -172,7 +184,7 @@ export const Link = Mark.create({ defaultProtocol: DEFAULT_PROTOCOL, type: this.type, shouldAutoLink, - }) + }), ); return plugins;