Building a custom sync provider in WordPress 7.0

WordPress 7.0 brings real-time collaboration to the block editor. The default HTTP polling transport is fine for most sites, but it will not hold up on a busy one. If you run WordPress hosting, or a site where three or four editors sit in the same post all day, sooner or later you will want to build a custom sync provider.

The default implementation syncs document state with periodic HTTP requests. That costs server load and adds latency you can feel. A WebSocket transport gets you closer to the “Google Docs” behavior clients actually ask for. I have closed enough “race condition” tickets to know what happens when four editors open the same post at once: a 1-second polling interval stops feeling like real time very quickly.

Why you need a custom sync provider

The default provider is written for compatibility rather than speed. It batches updates every four seconds, or every second when someone else is in the document. That runs on the cheapest shared host, which is the whole point of it, but nobody would call it real time. A custom sync provider lets you use WebSockets instead, so the browser pushes data when something actually changes rather than asking the server “any news?” every thousand milliseconds.

The WordPress 7.0 stability testing notes are worth reading first, for how the core team handles document state. It also helps to know that broken meta boxes can take out the sync flow completely.

The mechanics: Yjs and the sync manager

Real-time collaboration in WordPress runs on Yjs, a CRDT library. The sync provider is the transport layer that carries document updates. You swap the default transport with the sync.providers client-side filter, which expects a “provider creator”: an async function that opens your connection and hands back a cleanup method.

import { addFilter } from '@wordpress/hooks';
import { WebsocketProvider } from 'y-websocket';

addFilter( 'sync.providers', 'bbioon/websocket-sync', () => {
    return [
        async ( { objectType, objectId, ydoc, awareness } ) => {
            // Define a unique room for the post/entity
            const roomName = `${ objectType }-${ objectId ?? 'collection' }`;
            
            // Initialize the WebSocket provider
            const provider = new WebsocketProvider(
                'wss://sync.example.com',
                roomName,
                ydoc,
                { awareness }
            );

            return {
                destroy: () => provider.destroy(),
                on: ( event, callback ) => provider.on( event, callback ),
            };
        },
    ];
} );

Security: don’t ship naked connections

The code above is the happy path. Connect a client straight to a WebSocket server with no authorization and it will bite you. Your sync server sits outside the WordPress PHP lifecycle, so it has no idea who the user is unless you tell it, and that means token-based authentication.

The usual approach is to fetch a short-lived JWT from the WordPress REST API and pass it as a query parameter during the WebSocket handshake. If the token is invalid or expired, the server drops the connection on the spot. The WPVIP Real-Time Collaboration plugin is a good place to see an auth lifecycle that already ships to real sites.

Server-side validation checklist

  • Validate per document. “Logged in” is not enough, so check that the user holds edit_post for that specific ID.
  • Keep token TTLs short and re-authenticate on every reconnect.
  • Yjs handles a lot of abuse on its own, but still run basic integrity checks on updates before you trust them.

If this custom sync provider work is eating your dev hours, I can take it off your hands. I have been building on WordPress since the 4.x days and I know where these real-time systems tend to break.

Where the work actually is

A custom sync provider in WordPress 7.0 buys you lower latency and an editor that keeps up with the person typing into it. The sync.providers filter is the easy half. Your server infrastructure and your security model are where the time goes. If you build on y-websocket, plan for scaling the Node.js side horizontally, or you have traded an HTTP bottleneck for a WebSocket one.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.