Skip to main content

enableSingleEditorMode

Enables single editor mode, allowing only one user to edit the document at a time while others remain in read-only mode. Params
  • config: (object, optional). Configuration object for controlling single editor mode behavior
    • customMode: (boolean, optional). When true, SDK won’t automatically make HTML elements read-only for viewers. You need to handle this manually with the help of other APIs listed here. (default: false)
    • singleTabEditor: (boolean, optional). When enabled, restricts the editor to edit in only one browser tab at a time, preventing them from making changes across multiple tabs simultaneously (default: true)
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();

// Basic usage
liveStateSyncElement.enableSingleEditorMode();

// With configuration
liveStateSyncElement.enableSingleEditorMode({ 
    customMode: true,
    singleTabEditor: false 
});

disableSingleEditorMode

Disables single editor mode and returns to normal editing.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.disableSingleEditorMode();

Define Single Editor Mode Elements

Restrict to specific containers

  • Restrict Single Editor Mode to specific containers.
  • By default Single Editor Mode is enabled at the entire DOM level. You can restrict this feature to only certain HTML containers & their children by using this.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.singleEditorModeContainerIds(["rightPanel", "editor"]);

Fine tune elements control

Control which elements are controlled by Single Editor Mode.
You must add the data-velt-sync-access-* attributes to native HTML elements (e.g. button, input). It will not work directly on React components.
  • React / Next.js
  • Other Frameworks
// Enable sync access on custom elements
return (
    <div data-velt-sync-access="true">
        Controlled by Single Editor Mode
    </div>
);

// Exclude elements from sync access
return (
    <button data-velt-sync-access-disabled="true">
        Not controlled by Single Editor Mode
    </button>
);

Timeout Configuration

setEditorAccessTimeout

  • Configure automatic editor access timeout.
  • Default: 5 seconds.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.setEditorAccessTimeout(15); // in seconds

enableEditorAccessTransferOnTimeOut

  • When editor access timeout is reached, automatically transfer editor access to the next user in the queue.
  • Enabled by default.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.enableEditorAccessTransferOnTimeOut();

disableEditorAccessTransferOnTimeOut

  • When editor access timeout is reached, do not automatically transfer editor access to the next user in the queue.
  • Enabled by default.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.disableEditorAccessTransferOnTimeOut();

getEditorAccessTimer

Track the state of editor access request timeout. Returns
  • EditorAccessTimer object:
    • state ('idle' | 'inProgress' | 'completed')
    • durationLeft (number)
  • React / Next.js
// Using Hooks
const editorAccessTimer = useEditorAccessTimer();

useEffect(() => {
    if (editorAccessTimer?.state === 'completed') {
        // Handle timeout completion
        if (isEditor) {
            acceptEditorAccessRequest();
        } else if (isRequester) {
            setUserAsEditor();
        }
    }
}, [editorAccessTimer]);

return (
    <div>
        Status: {editorAccessTimer?.state}
        Time Left: {editorAccessTimer?.durationLeft}s
    </div>
);


// Using API
const liveStateSyncElement = useLiveStateSyncUtils();
let subscription = liveStateSyncElement.getEditorAccessTimer().subscribe((editorAccessTimer) => {
    console.log('Editor Access Timer:', editorAccessTimer);
});

Auto-Sync Text Elements

  • Enable automatic syncing of text element contents across all users.
  • Supported elements:
    • <input>
    • <textarea>
    • ContentEditable <div>
  • First enable the feature and then define which elements should sync realtime.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();

// Enable auto-sync state
liveStateSyncElement.enableAutoSyncState();

// In your JSX
return (
    <textarea id="uniqueId" data-velt-sync-state="true"></textarea>
);

Editor

setUserAsEditor

Sets the current user as the editor, making all other users read-only.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.setUserAsEditor();

Error handling

setUserAsEditor includes validations to prevent assigning an editor when one already exists. It returns a promise that resolves to an optional error object.
  • React / Next.js
  • Other Frameworks
export interface SetUserAsEditorResponse {
  error?: ErrorEvent;
}

export type ErrorEvent = {
  error?: string;
  code: string;
  message?: string;
  source?: string;
};

// Usage
const liveStateSyncElement = useLiveStateSyncUtils();
const result = await liveStateSyncElement.setUserAsEditor();
if (result?.error) {
  if (result.error.code === 'same_user_editor_current_tab') {
    // Same user already editor in current tab
  } else if (result.error.code === 'same_user_editor_different_tab') {
    // Same user already editor in different tab
  } else if (result.error.code === 'another_user_editor') {
    // Another user already editor
  }
}
Possible error values:
{
  code: 'same_user_editor_current_tab',
  message: 'Same user is already editor in current tab.',
  source: 'setUserAsEditor',
}
{
  code: 'same_user_editor_different_tab',
  message: 'Same user is already editor in different tab.',
  source: 'setUserAsEditor',
}
{
  code: 'another_user_editor',
  message: 'Another user is already editor.',
  source: 'setUserAsEditor',
}

isUserEditor

  • Get the current user’s editor status.
Returns
  • null: When the state is not available yet.
  • undefined: When there are no current editors available in single editor mode.
  • UserEditorAccess: When there is at least one editor available in single editor mode.
    • isEditor (boolean) - Whether the user is the editor
    • isEditorOnCurrentTab (boolean) - Whether the user is editor on current tab
  • React / Next.js
  • Other Frameworks
// Using Hooks
const { isEditor, isEditorOnCurrentTab } = useUserEditorState();

// Using API
const liveStateSyncElement = useLiveStateSyncUtils();
let subscription = liveStateSyncElement.isUserEditor().subscribe((userEditorAccess) => {
    console.log('Is Editor:', userEditorAccess.isEditor);
    console.log('Is Editor on Current Tab:', userEditorAccess.isEditorOnCurrentTab);
});
To unsubscribe from the subscription:
subscription?.unsubscribe()

getEditor

Get information about the current editor. Returns
  • User object:
    • email (string) - Editor’s email
    • name (string) - Editor’s name
    • photoUrl (string) - Editor’s photo URL
    • userId (string) - Editor’s unique ID
  • React / Next.js
  • Other Frameworks
// Using Hooks
const editor = useEditor();

// Using API
const liveStateSyncElement = useLiveStateSyncUtils();
let subscription = liveStateSyncElement.getEditor().subscribe((user) => {
    console.log('Editor:', user);
});
To unsubscribe from the subscription:
subscription?.unsubscribe()

isEditorAccessRequested

Check if any viewer has requested editor access. Returns
  • null - User is not editor or request was canceled
  • EditorRequest object:
    • requestStatus (string) - ‘requested’ for active requests
    • requestedBy (User) - User object of the requester
  • React / Next.js
  • Other Frameworks
// Using Hooks
const editorAccessRequested = useEditorAccessRequestHandler();

// Using API
const liveStateSyncElement = useLiveStateSyncUtils();
let subscription = liveStateSyncElement.isEditorAccessRequested().subscribe((data) => {
    if (data === null) {
        console.log('No active requests or user is not editor');
    } else {
        console.log('Request from:', data.requestedBy.name);
    }
});
To unsubscribe from the subscription:
subscription?.unsubscribe()

acceptEditorAccessRequest

Accept editor access requests.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.acceptEditorAccessRequest();

rejectEditorAccessRequest

Reject editor access requests.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.rejectEditorAccessRequest();

editCurrentTab

Make current tab editable when editor has multiple tabs open.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.editCurrentTab();

Viewer

requestEditorAccess

Request editor access from the current editor. Returns
  • null - Request is pending
  • true - Request accepted
  • false - Request rejected
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
let subscription = liveStateSyncElement.requestEditorAccess().subscribe((status) => {
    if (status === null) console.log('Request pending');
    else if (status === true) console.log('Request accepted');
    else console.log('Request rejected');
});
To unsubscribe from the subscription:
subscription?.unsubscribe()

cancelEditorAccessRequest

Cancel the editor access request.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.cancelEditorAccessRequest();

Heartbeat

The heartbeat mechanism provides more reliable presence detection in single editor mode scenarios. This feature is enabled by default when single editor mode is enabled.
If you want to disable heartbeat functionality, you must disable it before enabling single editor mode.

enableHeartbeat

Enables/Disables the heartbeat mechanism for Single Editor Mode presence detection. Default: enabled
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.enableHeartbeat();
liveStateSyncElement.disableHeartbeat();

Presence Heartbeat

updateUserPresence

Send a lightweight heartbeat from your host app to Velt. Velt will use that as a fallback in rare edge cases if it fails to detect multi‑tab/device presence. Most apps don’t need this; use only if you see ambiguity in who’s the active editor.
  • React / Next.js
  • Other Frameworks
export interface LiveStateSingleEditorExternalUserPresence {
  /** True if the same user is present on a different tab. */
  sameUserPresentOnTab?: boolean;
  /** True if a different user is present on a different tab. */
  differentUserPresentOnTab?: boolean;
  /** User IDs of users present on a different tab. */
  userIds?: string[];
}

const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.updateUserPresence({
  sameUserPresentOnTab: false,
  differentUserPresentOnTab: true,
  userIds: ['user-2']
});

resetUserAccess

Reset editor access for all users.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.resetUserAccess();

UI

enableDefaultSingleEditorUI

  • Control the visibility of the default Single Editor Mode System UI.
  • The default UI shows:
    • Current user’s editor/viewer status
    • Editor access requests
    • Request timeout countdown
    • Request rejection options
  • If you disable the default UI, you’ll need to implement your own UI for these features.
  • React / Next.js
  • Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();

// Enable default UI (enabled by default)
liveStateSyncElement.enableDefaultSingleEditorUI();

// Disable default UI for custom implementation
liveStateSyncElement.disableDefaultSingleEditorUI();

Embed Single Editor Mode Panel

  • Embed the Single Editor Mode Panel into your UI.
  • React / Next.js
  • Other Frameworks
<VeltSingleEditorModePanel shadowDom={false} darkMode={true} variant="custom-ui" />

Event Subscription

on

Subscribe to Single Editor Mode Events. Here is the list of events you can subscribe to and the event objects you will receive.
CategoryEvent TypeDescriptionEvent Object
EditoraccessRequestedWhen a user requests editor access. Editor will get this event.AccessRequestEvent
EditoraccessRequestCanceledWhen a user cancels their request for editor access. Editor will get this event.AccessRequestEvent
VieweraccessAcceptedWhen a user’s request for editor access is accepted. Requesting Viewer will get this event.AccessRequestEvent
VieweraccessRejectedWhen a user’s request for editor access is rejected. Requesting Viewer will get this event.AccessRequestEvent
Access AssignmenteditorAssignedWhen a user is assigned as the editor.SEMEvent
Access AssignmentviewerAssignedWhen a user is assigned as a viewer.SEMEvent
EditoreditorOnDifferentTabDetectedWhen the editor opens the same document in a different browser tab. Editor will get this event.SEMEvent
  • React / Next.js
  • Other Frameworks
Using Hooks:
// Replace "EVENT_TYPE" with an actual event string. eg: "accessRequested".
const eventData = useLiveStateSyncEventCallback("EVENT_TYPE"); 

useEffect(() => {
  if (eventData) { 
    console.log("EVENT_TYPE data: ", eventData);
  }
}, [eventData]);
Using API:
const liveStateSyncElement = useLiveStateSyncUtils();
// Replace "EVENT_TYPE" with an actual event string. eg: "accessRequested".
liveStateSyncElement.on("EVENT_TYPE").subscribe((eventData) => {
  console.log("EVENT_TYPE data: ", eventData);
});

Best Practices

  • Use singleTabEditor to prevent confusion when users have multiple tabs open
  • Add IDs to HTML elements with sync attributes for more robust syncing
  • Only apply sync attributes to native HTML elements, not framework components
I