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
});
// Get LiveStateSyncElement from Velt Client
const liveStateSyncElement = Velt.getLiveStateSyncElement();
// 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();
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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"]);
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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>
);
<!-- Enable sync access on custom elements -->
<div data-velt-sync-access="true">
Controlled by Single Editor Mode
</div>
<!-- Exclude elements from sync access -->
<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
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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();
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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();
const liveStateSyncElement = Velt.getLiveStateSyncElement();
liveStateSyncElement.disableEditorAccessTransferOnTimeOut();
getEditorAccessTimer
Track the state of editor access request timeout.
Returns
EditorAccessTimer object:
state ('idle' | 'inProgress' | 'completed')
durationLeft (number)
// 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>
);
<script>
const liveStateSyncElement = Velt.getLiveStateSyncElement();
liveStateSyncElement.enableAutoSyncState();
</script>
<body>
<!-- Enable auto-sync on text elements -->
<textarea id="uniqueId" data-velt-sync-state="true"></textarea>
</body>
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();
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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
}
}
const liveStateSyncElement = Velt.getLiveStateSyncElement();
const result = await liveStateSyncElement.setUserAsEditor();
if (result && 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()
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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()
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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()
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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();
const liveStateSyncElement = Velt.getLiveStateSyncElement();
liveStateSyncElement.acceptEditorAccessRequest();
rejectEditorAccessRequest
Reject editor access requests.
React / Next.js
Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.rejectEditorAccessRequest();
const liveStateSyncElement = Velt.getLiveStateSyncElement();
liveStateSyncElement.rejectEditorAccessRequest();
editCurrentTab
Make current tab editable when editor has multiple tabs open.
React / Next.js
Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.editCurrentTab();
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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()
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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();
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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.
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();
const liveStateSyncElement = Velt.getLiveStateSyncElement();
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']
});
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 different tabs. */
userIds?: string[];
}
// API method
const liveStateSyncElement = Velt.getLiveStateSyncElement();
const userPresenceObject = {
sameUserPresentOnTab: false,
differentUserPresentOnTab: true,
userIds: ['user-2']
};
liveStateSyncElement.updateUserPresence(userPresenceObject);
resetUserAccess
Reset editor access for all users.
React / Next.js
Other Frameworks
const liveStateSyncElement = useLiveStateSyncUtils();
liveStateSyncElement.resetUserAccess();
const liveStateSyncElement = Velt.getLiveStateSyncElement();
liveStateSyncElement.resetUserAccess();
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();
const liveStateSyncElement = Velt.getLiveStateSyncElement();
// 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" />
<velt-single-editor-mode-panel shadow-dom="false" dark-mode="true" variant="custom-ui"></velt-single-editor-mode-panel>
Event Subscription
Subscribe to Single Editor Mode Events. Here is the list of events you can subscribe to and the event objects you will receive.
| Category | Event Type | Description | Event Object |
|---|
| Editor | accessRequested | When a user requests editor access. Editor will get this event. | AccessRequestEvent |
| Editor | accessRequestCanceled | When a user cancels their request for editor access. Editor will get this event. | AccessRequestEvent |
| Viewer | accessAccepted | When a user’s request for editor access is accepted. Requesting Viewer will get this event. | AccessRequestEvent |
| Viewer | accessRejected | When a user’s request for editor access is rejected. Requesting Viewer will get this event. | AccessRequestEvent |
| Access Assignment | editorAssigned | When a user is assigned as the editor. | SEMEvent |
| Access Assignment | viewerAssigned | When a user is assigned as a viewer. | SEMEvent |
| Editor | editorOnDifferentTabDetected | When 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);
});
const liveStateSyncElement = Velt.getLiveStateSyncElement();
// 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