tiptap: Uncaught TypeError: ystate is undefined when using Collaboration and CollaborationCursor and esbuild

What’s the bug you are facing?

Hi there. I’m using esbuild as the build system in my app, and found that I could not use both Collaboration and CollaborationCursor together without the app error-ing out with an Uncaught TypeError: ystate is undefined error. The code builds properly, and produces this error at runtime.

Investigation suggests that this is because both plugins are creating their own instances of the y-sync plugin, and then the creation of yState fails the second time because the key no longer matches the one in the state, resulting in an undefined yState.

image

Which browser was this experienced in? Are any special extensions installed?

Firefox 102.0.1, Chrome v103. No special extensions.

How can we reproduce the bug on our side?

A minimal repro is to take the code from the CollaborationCursor example from the documentation site, and use esbuild to bundle it (with the bundle: true param). The bundle should be produced successfully, but trying to run the bundle will fail.

I have a demo repository with that minimal repro here: https://github.com/a-morphous/tiptap-esbuild-collab-issue/

You can run npm run build and then npm run serve to see the error locally. Commenting out the use of CollaborationCursor will make the code in the repository run as expected.

Can you provide a CodeSandbox?

Unlikely, given that this is most likely a build issue; I’d be happy to try to create one if it would help, though.

What did you expect to happen?

The app builds and runs without producing any errors.

Anything to add? (optional)

It looks like the schema for adding multiple plugins into tiptap (or maybe prosemirror or yjs?) is causing a name mismatch with the state, as shown when I set a breakpoint when yState gets set:

image

It doesn’t error out if I take out the CollaborationCursor plugin, because the first time y-sync is created, the key and state names match; it’s only the second one onwards that fails.

Did you update your dependencies?

  • Yes, I’ve updated my dependencies to use the latest version of all packages.

Are you sponsoring us?

  • Yes, I’m a sponsor. 💖

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 16 (2 by maintainers)

Most upvoted comments

'use client'

import './styles.css'
import { useEffect, useState } from 'react'

import * as Y from 'yjs'
import { HocuspocusProvider } from '@hocuspocus/provider'
import { useEditor, EditorContent } from '@tiptap/react'

import Collaboration from '@tiptap/extension-collaboration'
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
import Placeholder from '@tiptap/extension-placeholder'
import StarterKit from '@tiptap/starter-kit'

const Tiptap = () => {

  const [provider, setProvider] = useState<HocuspocusProvider>()

  useEffect(() => {
    setProvider(
      new HocuspocusProvider({
        document: new Y.Doc(),
        name: 'document',
        url: 'ws://localhost:1234'
      })
    )
  }, [])

  const extensions = provider ? [
    Collaboration.configure({ document: provider.document }),
    CollaborationCursor.configure({
      provider,
      user: { color: '#3daee9', name: 'John Doe' },
    }),
    Placeholder.configure({ placeholder: 'Start typing...' }),
    StarterKit.configure({ history: false })
  ] : [Placeholder.configure({ placeholder: 'Loading...' }), StarterKit]

  const editor = useEditor({ extensions }, [provider])
  return <EditorContent editor={editor} />
}

export default Tiptap

Hey there!

I know the Tiptap collaborative extension documentation can be a bit tricky, but don’t worry! If you’re looking to implement it on a React-based framework, the code I provided earlier is a great starting point.

It’s a basic setup that you can easily customize to meet your specific needs. And since it’s built with React, it will be a breeze to integrate with any React-based framework, including Next.js.

Oh, and here’s a quick styles.css file to get you going! 💻

I hope this helps you get started! If you have any other questions or need further clarification, don’t hesitate to ask. Good luck and happy coding! 😎

.ProseMirror > * + * {
  margin-top: 0.75em;
}

.ProseMirror p.is-editor-empty:first-child::before {
  color: #adb5bd;
  content: attr(data-placeholder);
  float: left;
  height: 0;
  pointer-events: none;
}

.collaboration-cursor__caret {
  border-left: 1px solid #0d0d0d;
  border-right: 1px solid #0d0d0d;
  margin-left: -1px;
  margin-right: -1px;
  pointer-events: none;
  position: relative;
  word-break: normal;
}

.collaboration-cursor__label {
  border-radius: 3px 3px 3px 0;
  color: #0d0d0d;
  font-size: 12px;
  font-weight: 600;
  left: -1px;
  padding: 0.1rem 0.3rem;
  position: absolute;
  top: -1.4em;
  user-select: none;
  white-space: nowrap;
}

Thank you, @mmilad and @nmante. I finally got it working by pinning y-prosemirror to 1.0.20.

This is likely due to dependency mismatches. I had y-prosemirror installed with version 1.2.0, and tiptap 2.0.0-beta.218 packages. I was getting the ystate undefined error. The fix was to pin the y-prosemirror version to 1.0.20 like @mmilad said. Here’s my package.js for anyone curious. The relevant packages are tiptap y-* packages as well as yjs.

{
  "dependencies": {
    "@auth0/auth0-spa-js": "^1.16.0",
    "@emotion/cache": "^11.4.0",
    "@emotion/react": "^11.10.5",
    "@emotion/styled": "^11.10.5",
    "@hocuspocus/provider": "^1.1.0",
    "@material-ui/core": "^5.0.0-beta.2",
    "@material-ui/icons": "^5.0.0-beta.1",
    "@material-ui/lab": "^5.0.0-alpha.41",
    "@mui/icons-material": "^5.11.0",
    "@mui/lab": "^5.0.0-alpha.106",
    "@mui/material": "^5.11.4",
    "@react-pdf/renderer": "^2.0.16",
    "@sentry/integrations": "^7.15.0",
    "@sentry/react": "^7.15.0",
    "@sentry/tracing": "^7.15.0",
    "@tiptap/core": "^2.0.0-beta.218",
    "@tiptap/extension-collaboration": "^2.0.0-beta.218",
    "@tiptap/extension-collaboration-cursor": "^2.0.0-beta.218",
    "@tiptap/extension-image": "^2.0.0-beta.218",
    "@tiptap/extension-link": "^2.0.0-beta.218",
    "@tiptap/extension-mention": "^2.0.0-beta.218",
    "@tiptap/extension-ordered-list": "^2.0.0-beta.218",
    "@tiptap/extension-paragraph": "^2.0.0-beta.218",
    "@tiptap/extension-placeholder": "^2.0.0-beta.218",
    "@tiptap/extension-youtube": "^2.0.0-beta.218",
    "@tiptap/pm": "^2.0.0-beta.218",
    "@tiptap/react": "^2.0.0-beta.218",
    "@tiptap/starter-kit": "^2.0.0-beta.218",
    "@tiptap/suggestion": "^2.0.0-beta.218",
    "@types/react": "^18.0.27",
    "@typescript-eslint/eslint-plugin": "^5.49.0",
    "@typescript-eslint/parser": "^5.49.0",
    "apexcharts": "^3.27.1",
    "aws-amplify": "^4.1.0",
    "compressorjs": "^1.1.1",
    "css-to-react-native": "^3.0.0",
    "date-fns": "^2.22.1",
    "dompurify": "^2.4.0",
    "emoji-essential": "^2.0.0",
    "emoji-picker-react": "^4.4.3",
    "firebase": "^8.7.0",
    "formik": "^2.2.9",
    "gray-matter": "^4.0.3",
    "history": "^5.0.0",
    "html-react-parser": "^3.0.1",
    "i18next": "^20.3.5",
    "lodash": "^4.17.21",
    "lodash.debounce": "^4.0.8",
    "nprogress": "^0.2.0",
    "numeral": "^2.0.6",
    "parchment": "^2.0.1",
    "prop-types": "^15.7.2",
    "prosemirror-state": "^1.4.2",
    "quill": "1.3.6",
    "quill-image-drop-and-paste": "^1.2.14",
    "quill-image-resize": "^3.0.9",
    "quill-image-uploader": "^1.2.3",
    "react": "^17.0.2",
    "react-apexcharts": "^1.3.9",
    "react-beautiful-dnd": "^13.1.1",
    "react-collapsible": "^2.10.0",
    "react-cool-onclickoutside": "^1.7.0",
    "react-dom": "^17.0.2",
    "react-dropzone": "^11.3.2",
    "react-fast-compare": "^3.2.0",
    "react-fast-image": "^0.0.30",
    "react-giphy-searchbox": "^1.5.4",
    "react-google-button": "^0.7.2",
    "react-helmet-async": "^1.0.9",
    "react-hot-toast": "^2.0.0",
    "react-i18next": "^11.11.4",
    "react-markdown": "^5.0.3",
    "react-quill": "^1.3.5",
    "react-router": "6.0.0-beta.6",
    "react-router-dom": "6.0.0-beta.6",
    "react-router-hash-link": "^2.4.3",
    "react-syntax-highlighter": "^15.4.3",
    "sass": "^1.56.1",
    "simplebar": "^5.3.3",
    "simplebar-react": "^2.3.3",
    "stripe": "^9.12.0",
    "style-loader": "^3.3.1",
    "stylis-plugin-rtl": "^2.1.0",
    "tippy.js": "^6.3.7",
    "typescript": "^4.9.4",
    "use-places-autocomplete": "^4.0.0",
    "uuid": "^9.0.0",
    "y-prosemirror": "1.0.20",
    "y-protocols": "^1.0.5",
    "yjs": "^13.5.47",
    "yup": "^0.32.9"
  },
  "devDependencies": {
    "@babel/core": "^7.14.8",
    "@babel/eslint-parser": "^7.14.7",
    "@babel/preset-react": "^7.14.5",
    "@types/google.maps": "^3.50.4",
    "eslint": "^7.31.0",
    "eslint-config-airbnb": "^18.2.1",
    "eslint-config-airbnb-typescript": "^12.3.1",
    "eslint-plugin-import": "^2.23.4",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-react": "^7.24.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "npm-run-all": "^4.1.5",
    "react-scripts": "4.0.3"
  }
}

Have the same issue here. I copy / pasted the code in this example, which is working fine until I try to use @tiptap/extension-collaboration-cursor extension. Tried in versions 2.0.0-beta.214 - 2.0.0-beta.218.

Getting the same error as @adrice727.

I also tried to install the y-prosemirror dependency (1.2.0) without any success. After taking a look into the peer deps of @tiptap/extension-collaboration-cursor and @tiptap/extension-collaboration-cursor, i saw the the y-prosemirror version requested as peerDeps are 1.0.20. Worked after installed that version.

Thank @a-morphous

Yup that works! Thank you for the helpful guide @ owynreveon and info @ a-morphous, much appreciated. I have collaboration cursor working in all my environments (React and Vite) now.