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_postfor 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.