Leveraging TipTap.dev for building a joint editing platform -

I have integrated the collaboration feature from tiptap.dev into my NextJS application.

Initially, I used their CLI command for the Hocuspocus server which worked well on port 1234 locally and synchronized text editing across browsers seamlessly. However, I encountered an issue when I realized I needed a proper server for the frontend to connect to as it will be hosted on Vercel.

After following the documentation and setting up an Express server using code provided by tiptap.dev, I found that the frontend was not connecting as expected. I am now stuck trying to figure out what may be missing or incorrect in my setup.

You can find the express code I used here:

I suspect my struggle might be related to not fully grasping how websockets function.

Below is the frontend code snippet:

import Collaboration from "@tiptap/extension-collaboration"

import StarterKit from "@tiptap/starter-kit"
import { EditorContent, useEditor } from "@tiptap/react"
import React from "react"
import { HocuspocusProvider } from "@hocuspocus/provider"
import * as Y from "yjs"

// A new Y document
const ydoc = new Y.Doc()
// Registered with a WebRTC provider
// const provider = new WebrtcProvider('example-document', ydoc)

const provider = new HocuspocusProvider({
    url: "ws://127.0.0.1:1234",
    name: "example-document",
})

const TipTapEditor = () => {

    const editor = useEditor({
        extensions: [
            StarterKit.configure({
                // The Collaboration extension comes with its own history handling
                history: false,
            }),
            // Register the document with Tiptap
            Collaboration.configure({
                document: provider.document,
            }),
        ],
    })

    return (
        <>
            <EditorContent
                className="w-full h-full border border-black"
                editor={editor}
            />
        </>
    )
}

export default TipTapEditor

And this is the Express server setup:

const express = require("express")
const expressWebsockets = require("express-ws")
const cors = require("cors")
const bodyParser = require("body-parser")
const { Server } = require("@hocuspocus/server")

// Configure Hocuspocus
const server = Server.configure({
    // ...
})

// Setup your express instance using the express-ws extension
const { app } = expressWebsockets(express())

app.use(cors())
app.use(bodyParser.json())

// A basic http route
app.get("/", (request, response) => {
    response.send("Hello World!")
})

app.post("/", (req, res) => {
    res.status(200).json({msg: "success"})
})

// Add a websocket route for Hocuspocus
// Note: make sure to include a parameter for the document name.
// You can set any contextual data like in the onConnect hook
// and pass it to the handleConnection method.
app.ws("/collaboration/:document", (websocket, request) => {
    console.log("ws: ", websocket)
    console.log("req: ", request)
    const context = {
        user: {
            id: 1234,
            name: "Jane",
        },
    }

    server.handleConnection(websocket, request, context)
})

// Start the server
app.listen(3005, () => console.log("Listening on port 3005"))

Answer №1

It seems like there are a couple of issues that need addressing.

  1. The URL specified in your frontend
    url: "ws://127.0.0.1:1234"
    is pointing to your local host IP rather than the IP of a websocket server.
  2. Your websocket server is set up to listen on port 3005, so even if you corrected the base IP in your frontend, the port number would still not match.

You will need to determine the IP address of your websocket server and update your frontend to point to it correctly. Also, ensure that your WSS is operational, as NextJS API routes are serverless which could create complications.

Best of luck with resolving these issues!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can you place app.use within app.get in Node.js?

app.get('/game/', function(req, res) { if (req.session.user) { app.use(express.static(__dirname + '/game')); res.sendFile(__dirname + '/game/index.html'); } else { res.send('not logged in '); } } ...

Two values are returned from the Node.js Mongoose Exports function

Currently, I am encountering an issue with my project where a service I developed is up and running. However, it is not providing the desired value as a response. Specifically, the goal is to generate coffee items linked to specific companies. Whenever new ...

Utilizing Next.js to create a Higher Order Component (HOC) for fetching data from a REST API using Typescript is proving to be a challenge, as the

In my withUser.tsx file, I have implemented a higher order component that wraps authenticated pages. This HOC ensures that only users with a specified user role have access to the intended pages. import axios, { AxiosError } from "axios"; import ...

Steps to efficiently enumerate the array of parameters in the NextJS router:

In my NextJS application, I have implemented a catch all route that uses the following code: import { useRouter} from 'next/router' This code snippet retrieves all the parameters from the URL path: const { params = [] } = router.query When I co ...

Sending a POST request in Node.js and Express may result in the request body being empty or undefined

Here is a snippet of my Typescript code: import express = require('express'); const app: express.Application = express(); const port: number = 3000; app.listen(port, () => { console.log("The server is now running on port" + port); ...

What is the most effective way to securely store a Token for Firebase cloud functions?

Currently, I have a Firebase cloud function that utilizes express middleware to generate an authToken and passes it through the routes. This token has a 24-hour lifespan, so to avoid overloading the service with excessive requests, I am looking to cache th ...

Cursor hovers over button, positioned perfectly in the center

I am facing a challenge with implementing a hover function for a set of buttons on the screen. The goal is to display the mouse pointer at the center of the button upon hovering, rather than the actual position of the cursor being displayed. I have tried u ...

Tips for displaying lesser-known checkboxes upon clicking a button in Angular

I have a form with 15 checkboxes, but only 3 are the most popular. I would like to display these 3 by default and have an icon at the end to expand and collapse the rest of the checkboxes. Since I'm using Angular for my website, I think I can simply ...

Include onload to element without running it in Puppeteer

I am currently developing a web scraping tool using Puppeteer. Once the webpage is fully loaded, I need to update the HTML code and include an onload event for certain elements. The issue is that in Puppeteer, the onload event is actually triggered automa ...

Is it possible to integrate the `lnurl` npm library with Nextjs14?

When starting the first boilerplate project in Nextjs14, remember to include the following command: npm i lnurl However, after installation and attempting to use the API by importing 'lnurl', an error occurs as shown below: Import trace for requ ...

Experiencing issues with the redirect button on the navigation bar of my website

Video: https://youtu.be/aOtayR8LOuc It is essential that when I click a button on my navigation bar, it will navigate to the correct page. However, since the same nav bar is present on each page, it sometimes tries to redirect to the current page multiple ...

Encountering a strange issue when attempting to link app.js with mongodb

I recently set up MongoDB and the Compass, everything was working fine until I tried to connect MongoDB with my app.js. Now I'm getting a strange error message. Could anyone help me understand what this error means and how I can fix it? Unfortunately, ...

Preserve content from a hyperlink using JavaScript

How can I store a value to the cache using JavaScript when clicking on an anchor link? The code below sets up the dynamic content: <a align="left" href="projectoverview.html">Test Manager</a> I want to save the text "Test Manager" to the cach ...

Adding HTML elements dynamically using jQuery: A how-to guide

My objective is to start with one element upon loading the page, and this element should have an ID of "something_O". When the user clicks on an add link, a new identical HTML element should be added underneath the existing element. The new element should ...

Capturing C# log data for JavaScript interactions

Can anyone provide recommendations on how to capture JavaScript interactions in a browser using C#? I am interested in developing a web crawler that can track these interactions, allowing me to search for potentially harmful calls. ...

How to open a new tab in ReactJS

When attempting to open a component in a new tab using the Link in React router, it results in a 404 error page being displayed instead of the desired react component. The main React entry point file (js), import React from 'react'; import { re ...

Encountered an issue while installing @reach/router on React version 17

Recently I started working on a project using React and decided to incorporate @reach/router. However, during the installation process in the command line, an error popped up: npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ...

What is the method for assigning a class name to a child element within a parent element?

<custom-img class="decrease-item" img-src="images/minus-green.svg" @click="event => callDecreaseItem(event, itemId)" /> Here we have the code snippet where an image component is being referenced. <template&g ...

In TypeScript, use a Record<string, any> to convert to {name: string}

I have developed a custom react hook to handle API calls: const useFetch: (string) => Record<string, any> | null = (path: string) => { const [data, setData] = useState<Record<string, any> | null>(null); var requestOptions: Requ ...

Navigating to the parent Vue component in a Vue3 project with Composition API structure in WebStorm

After transitioning to Vue3 and embracing the Composition API style, I find myself missing a small convenience that I had when developing in the previous Options API pattern. In WebStorm/IntelliJ IDE, I used to be able to command-click (Mac) on the "export ...