diff --git a/src/mentions/plugin.js b/src/mentions/plugin.js
new file mode 100644
index 0000000..ab72630
--- /dev/null
+++ b/src/mentions/plugin.js
@@ -0,0 +1,136 @@
+/**
+ * This file is a modified version of prosemirror-suggestions
+ * https://github.com/quartzy/prosemirror-suggestions/blob/master/src/suggestions.js
+ */
+
+import { Plugin, PluginKey } from 'prosemirror-state';
+import { Decoration, DecorationSet } from 'prosemirror-view';
+
+export const triggerCharacters = char => $position => {
+  const regexp = new RegExp(`(?:^)?${char}[^\\s${char}]*`, 'g');
+
+  const textFrom = $position.before();
+  const textTo = $position.end();
+
+  const text = $position.doc.textBetween(textFrom, textTo, '\0', '\0');
+
+  let match;
+
+  // eslint-disable-next-line
+  while ((match = regexp.exec(text))) {
+    const prefix = match.input.slice(Math.max(0, match.index - 1), match.index);
+    if (!/^[\s\0]?$/.test(prefix)) {
+      // eslint-disable-next-line
+      continue;
+    }
+
+    const from = match.index + $position.start();
+    let to = from + match[0].length;
+
+    if (from < $position.pos && to >= $position.pos) {
+      return { range: { from, to }, text: match[0] };
+    }
+  }
+  return null;
+};
+
+export const suggestionsPlugin = ({
+  matcher,
+  suggestionClass = 'prosemirror-mention-node',
+  onEnter = () => false,
+  onChange = () => false,
+  onExit = () => false,
+  onKeyDown = () => false,
+}) => {
+  return new Plugin({
+    key: new PluginKey('mentions'),
+
+    view() {
+      return {
+        update: (view, prevState) => {
+          const prev = this.key.getState(prevState);
+          const next = this.key.getState(view.state);
+
+          const moved =
+            prev.active && next.active && prev.range.from !== next.range.from;
+          const started = !prev.active && next.active;
+          const stopped = prev.active && !next.active;
+          const changed = !started && !stopped && prev.text !== next.text;
+
+          if (stopped || moved)
+            onExit({ view, range: prev.range, text: prev.text });
+          if (changed && !moved)
+            onChange({ view, range: next.range, text: next.text });
+          if (started || moved)
+            onEnter({ view, range: next.range, text: next.text });
+        },
+      };
+    },
+
+    state: {
+      init() {
+        return {
+          active: false,
+          range: {},
+          text: null,
+        };
+      },
+
+      apply(tr, prev) {
+        const { selection } = tr;
+        const next = { ...prev };
+
+        if (selection.from === selection.to) {
+          if (
+            selection.from < prev.range.from ||
+            selection.from > prev.range.to
+          ) {
+            next.active = false;
+          }
+
+          const $position = selection.$from;
+          const match = matcher($position);
+
+          if (match) {
+            next.active = true;
+            next.range = match.range;
+            next.text = match.text;
+          } else {
+            next.active = false;
+          }
+        } else {
+          next.active = false;
+        }
+
+        if (!next.active) {
+          next.range = {};
+          next.text = null;
+        }
+
+        return next;
+      },
+    },
+
+    props: {
+      handleKeyDown(view, event) {
+        const { active } = this.getState(view.state);
+
+        if (!active) return false;
+
+        return onKeyDown({ view, event });
+      },
+      decorations(editorState) {
+        const { active, range } = this.getState(editorState);
+
+        if (!active) return null;
+
+        return DecorationSet.create(editorState.doc, [
+          Decoration.inline(range.from, range.to, {
+            nodeName: 'span',
+            class: suggestionClass,
+          }),
+        ]);
+      },
+    },
+  });
+};
diff --git a/src/mentions/schema.js b/src/mentions/schema.js
new file mode 100644
index 0000000..b0ed3b7
--- /dev/null
+++ b/src/mentions/schema.js
@@ -0,0 +1,84 @@
+import { EditorState } from 'prosemirror-state';
+import {
+  schema,
+  defaultMarkdownParser,
+  MarkdownParser,
+  MarkdownSerializer,
+} from 'prosemirror-markdown';
+import { wootWriterSetup } from '@chatwoot/prosemirror-schema';
+
+import { Schema } from 'prosemirror-model';
+
+const mentionParser = () => ({
+  node: 'mention',
+  getAttrs: ({ mention }) => {
+    const { userId, userFullName } = mention;
+    return { userId, userFullName };
+  },
+});
+
+const markdownSerializer = () => (state, node) => {
+  const uri = state.esc(
+    `mention://user/${node.attrs.userId}/${node.attrs.userFullName}`
+  );
+  const escapedDisplayName = state.esc('@' + (node.attrs.userFullName || ''));
+
+  state.write(`[${escapedDisplayName}](${uri})`);
+};
+
+export const addMentionsToMarkdownSerializer = serializer =>
+  new MarkdownSerializer(
+    { mention: markdownSerializer(), ...serializer.nodes },
+    serializer.marks
+  );
+
+const mentionNode = {
+  attrs: { userFullName: { default: '' }, userId: { default: '' } },
+  group: 'inline',
+  inline: true,
+  selectable: true,
+  draggable: true,
+  atom: true,
+  toDOM: node => [
+    'span',
+    {
+      class: 'prosemirror-mention-node',
+      'mention-user-id': node.attrs.userId,
+      'mention-user-full-name': node.attrs.userFullName,
+    },
+    `@${node.attrs.userFullName}`,
+  ],
+  parseDOM: [
+    {
+      tag: 'span[mention-user-id][mention-user-full-name]',
+      getAttrs: dom => {
+        const userId = dom.getAttribute('mention-user-id');
+        const userFullName = dom.getAttribute('mention-user-full-name');
+        return { userId, userFullName };
+      },
+    },
+  ],
+};
+
+const addMentionNodes = nodes => nodes.append({ mention: mentionNode });
+
+const schemaWithMentions = new Schema({
+  nodes: addMentionNodes(schema.spec.nodes),
+  marks: schema.spec.marks,
+});
+const addMentionsToMarkdownParser = parser => {
+  return new MarkdownParser(schemaWithMentions, parser.tokenizer, {
+    ...parser.tokens,
+    mention: mentionParser(),
+  });
+};
+
+export const createState = (content, placeholder, plugins = []) =>
+  EditorState.create({
+    doc: addMentionsToMarkdownParser(defaultMarkdownParser).parse(content),
+    plugins: wootWriterSetup({
+      schema: schemaWithMentions,
+      placeholder,
+      plugins,
+    }),
+  });