
Research
Node.js Fixes AsyncLocalStorage Crash Bug That Could Take Down Production Servers
Node.js patched a crash bug where AsyncLocalStorage could cause stack overflows to bypass error handlers and terminate production servers.
@dotcms/uve
Advanced tools
Official JavaScript library for interacting with Universal Visual Editor (UVE)
The @dotcms/uve SDK adds live editing to your JavaScript app using the dotCMS Universal Visual Editor (UVE). It provides low-level tools that power our framework-specific SDKs, such as @dotcms/react and @dotcms/angular.
⚠️ We do not recommend using this SDK directly for most use cases, you should use a [framework SDK that handles setup](#Getting Started: Recommended Examples), rendering, and event wiring for you.
With @dotcms/uve, framework SDKs are able to:
We strongly recommend using one of our official framework SDKs, which are designed to handle UVE integration, routing, rendering, and more—out of the box. These examples are the best way to get started:
These examples handle UVE integration, routing, rendering, and more—out of the box. If you're building a headless dotCMS front-end, start there.
💡 We recommend using one of our official framework SDKs, which are designed to handle UVE integration, routing, rendering, and more—out of the box.
You can use @dotcms/uve directly, but it’s not recommended or supported unless you’re building a highly custom integration. Here’s how the pieces fit together:
@dotcms/client to fetch content and page data.data-dot-* attributes to containers and contentlets.Here's a minimal setup using @dotcms/client and @dotcms/uve:
// getPage.ts
import { createDotCMSClient } from '@dotcms/client';
import { initUVE, createUVESubscription } from '@dotcms/uve';
const dotCMSClient = createDotCMSClient({
dotcmsUrl: 'https://your-dotcms-instance.com',
authToken: 'your-api-key',
siteId: 'your-site-id'
});
const getPage = async () => {
const pageResponse = await dotCMSClient.page.get('/', {
languageId: '1'
});
return pageResponse;
};
⚠️ The
initUVE()function only works with aPageResponsereturned by@dotcms/client. If you try to pass in data from another source or build your own structure, it won't initialize properly.
import { initUVE, createUVESubscription } from '@dotcms/uve';
import { getPage } from './getPage';
const pageResponse = await getPage();
initUVE(pageResponse);
createUVESubscription('changes', (newPageResponse) => {
// Handle page updates (e.g. re-render)
});
⚠️ This only sets up the editor connection. You are responsible for rendering the page structure (rows, columns, containers, contentlets) using your own UI components.
// 🔧 Render the page layout (you must implement this component)
<MyDotCMSPage pageAsset={pageResponse.pageAsset} />
⚠️ Below is a simplified breakdown of how dotCMS layouts are structured and how you might render them manually.
📚 For a complete guide, here is a full tutorial: 👉 dotCMS Page Rendering Architecture
dotCMS pages are structured as nested layout objects:
PageAsset contains a layout objectlayout includes rows, columns, containers, and contentletsHere’s a basic pseudocode outline:
<Page>
{layout.body.rows.map(row => (
<Row>
{row.columns.map(column => (
<Column>
{column.containers.map(container => (
<Container data-dot-object="container" ...>
{container.contentlets.map(contentlet => (
<Contentlet data-dot-object="contentlet" ...>
{renderContentletByType(contentlet)}
</Contentlet>
))}
</Container>
))}
</Column>
))}
</Row>
))}
</Page>
Each contentlet is rendered according to its content type:
function renderContentletByType(contentlet) {
switch(contentlet.contentType) {
case 'text': return <TextBlock contentlet={contentlet} />;
case 'image': return <ImageBlock contentlet={contentlet} />;
case 'video': return <VideoBlock contentlet={contentlet} />;
default: return null;
}
}
To make the layout editable, be sure to apply all required data-dot-* attributes on containers and contentlets.
For Production Use:
For Testing & Development:
For Local Development:
For a step-by-step guide on setting up the Universal Visual Editor, check out our easy-to-follow instructions and get started in no time!
npm install @dotcms/uve@latest
All interfaces and types are available through the @dotcms/types package:
npm install @dotcms/types@latest --save-dev
The SDK uses several key types from @dotcms/types:
import {
DotCMSContentlet,
DotCMSPageResponse,
DotCMSUVEConfig,
DotCMSInlineEditingType,
UVEEventType,
UVEState
} from '@dotcms/types';
For a complete reference of all available types and interfaces, please refer to the @dotcms/types documentation.
initUVE(config?: DotCMSUVEConfig)initUVE is a function that initializes the Universal Visual Editor (UVE). It sets up the necessary communication between your app and the editor, enabling seamless integration and interaction.
| Input | Type | Required | Description |
|---|---|---|---|
config | DotCMSPageResponse | ✅ | The page Response from the @dotcms/client |
const { destroyUVESubscriptions } = initUVE(pageResponse);
⚠️ If you don't provide a
pageResponse, we can't assure that the UVE will be initialized correctly.
getUVEState()getUVEState is a function that returns the UVE state if UVE is active.
import { getUVEState } from '@dotcms/uve';
import { UVE_MODE } from '@dotcms/types';
const myEditButton = () => {
const uveState = getUVEState();
if (uveState?.mode === UVE_MODE.EDIT) {
return <button>Edit</button>;
}
return null;
};
dotCMSHost: The host URL of the DotCMS instanceexperimentId: The ID of the current experimentlanguageId: The language ID of the current page set on the UVEmode: The current editor mode ('preview', 'edit', 'live')persona: The persona of the current page set on the UVEpublishDate: The publish date of the current page set on the UVEvariantName: The name of the current variantcreateUVESubscription(eventType, callback)createUVESubscription is a function that allows your application to dynamically interact with UVE by subscribing to events such as content changes or navigation updates. This enables your app to respond in real-time to user actions and editor events, enhancing the interactive experience.
| Input | Type | Required | Description |
|---|---|---|---|
eventType | UVEEventType | ✅ | The event to subscribe to |
callback | Function | ✅ | Called when the event is triggered |
import { createUVESubscription } from '@dotcms/uve';
import { UVEEventType } from '@dotcms/types';
const sub = createUVESubscription(UVEEventType.CONTENT_CHANGES, (newPageResponse) => {
// do something when the content changes
});
// Later, when you want to unsubscribe
sub.unsubscribe();
UVEEventType.CONTENT_CHANGES: Triggered when the content of the page changes.UVEEventType.PAGE_RELOAD: Triggered when the page is reloaded.UVEEventType.REQUEST_BOUNDS: Triggered when the editor requests the bounds of the page.UVEEventType.IFRAME_SCROLL: Triggered when the iframe is scrolled.UVEEventType.IFRAME_SCROLL_END: Triggered when the iframe has stopped scrolling.UVEEventType.CONTENTLET_HOVERED: Triggered when a contentlet is hovered.editContentlet(contentlet)editContentlet is a function that opens the dotCMS modal editor for any contentlet in or out of page area.
| Input | Type | Required | Description |
|---|---|---|---|
contentlet | Contentlet<T> | ✅ | The contentlet you want to edit. |
import { editContentlet, getUVEState } from '@dotcms/uve';
import { UVE_MODE } from '@dotcms/types';
const myEditButton = ({ contentlet }) => {
const uveState = getUVEState();
if (uveState?.mode === UVE_MODE.EDIT) {
return <button onClick={() => editContentlet(contentlet)}>Edit</button>;
}
return null;
};
initInlineEditing(type, data)initInlineEditing is a function that triggers inline editing for supported field types (WYSIWYG or Block Editor).
| Input | Type | Required | Description |
|---|---|---|---|
type | DotCMSInlineEditingType | ✅ | 'BLOCK_EDITOR' or 'WYSIWYG' |
fieldData | DotCMSInlineEditingPayload | ✅ | Field content required to enable inline editing |
import { initInlineEditing, getUVEState } from "@dotcms/uve";
import { UVE_MODE } from "@dotcms/types";
const MyBanner = ({ contentlet }) => {
const uveState = getUVEState();
const handleClick = () => {
if (uveState?.mode === UVE_MODE.EDIT) {
const { inode, contentType, title } = contentlet;
initInlineEditing("BLOCK_EDITOR", {
inode,
contentType,
content: title,
fieldName: "title",
});
}
};
return (
<div>
<h1 onClick={handleClick}>{contentlet.title}</h1>
<p>{contentlet.description}</p>
</div>
);
};
inode (string): The inode of the contentlet to edit.contentType (string): The content type of the contentlet to edit.fieldName (string): The name of the field to edit.content (string): The content of the field to edit.enableBlockEditorInline(contentlet, fieldName)enableBlockEditorInline is a shortcut to enable inline block editing for a field.
| Input | Type | Required | Description |
|---|---|---|---|
contentlet | DotCMSBasicContentlet | ✅ | The target contentlet |
fieldName | string | ✅ | Name of the block field to edit |
import { enableBlockEditorInline, getUVEState } from '@dotcms/uve';
import { UVE_MODE } from '@dotcms/types';
const MyBanner = ({ contentlet }) => {
const uveState = getUVEState();
const handleClick = () => {
if (uveState?.mode === UVE_MODE.EDIT) {
enableBlockEditorInline(contentlet, 'blockContent');
}
};
return <MyBlockEditorRender onClick={handleClick} />;
};
updateNavigation(pathname)updateNavigation is a function that notifies UVE that navigation has changed (e.g., in SPAs).
| Input | Type | Required | Description |
|---|---|---|---|
pathname | string | ✅ | The new pathname to update |
import { updateNavigation } from '@dotcms/uve';
updateNavigation('/navigate-to-this-new-page');
reorderMenu(config?)reorderMenu is a function that opens the UVE menu editor to reorder navigation links.
| Input | Type | Required | Description |
|---|---|---|---|
config? | DotCMSReorderMenuConfig | ❌ | Optional config for reordering |
import { reorderMenu } from '@dotcms/uve';
reorderMenu({ startLevel: 2, depth: 3 });
startLevel (number): The level to start reordering fromdepth (number): The depth of the menu to reordersendMessageToUVE(message)sendMessageToUVE is a low-level function to send custom messages to UVE.
| Input | Type | Required | Description |
|---|---|---|---|
message | DotCMSUVEMessage<T> | ✅ | Object with action + payload |
sendMessageToUVE({
action: DotCMSUVEAction.CUSTOM_EVENT,
payload: { type: 'MyEvent', data: {...} }
});
| Event (DotCMSUVEAction) | Payload (T) |
| ---------------------------------- | ------------------------------------------------------------- | ------- |
| NAVIGATION_UPDATE | { url: string } |
| SET_BOUNDS | DotCMSContainerBound[] |
| SET_CONTENTLET | DotCMSBasicContentlet |
| IFRAME_SCROLL | 'up' | 'down' |
| IFRAME_SCROLL_END | --- |
| REORDER_MENU | DotCMSReorderMenuConfig |
| INIT_INLINE_EDITING | DotCMSInlineEditingPayload |
| COPY_CONTENTLET_INLINE_EDITING | { dataset: { inode, language, fieldName: this.fieldName } } |
| UPDATE_CONTENTLET_INLINE_EDITING | { content: string, dataset: { inode, langId, fieldName } } |
| GET_PAGE_DATA | --- |
| CLIENT_READY | --- |
| EDIT_CONTENTLET | DotCMSBasicContentlet |
destroyUVESubscriptions() on unmountdestroyUVESubscriptions() when your component unmounts to clean up subscriptionsgetUVEState() returns undefined
getUVEState()unsubscribe() method to prevent memory leaksinitInlineEditing() requires valid contentlet and field name
reorderMenu() must be called from a UI action
reorderMenu()reorderMenu() is triggered by a user action within the UIgetUVEState()If you're still experiencing problems after trying these solutions:
We offer multiple channels to get help with the dotCMS UVE SDK:
dotcms-uve when posting questionsWhen reporting issues, please include:
GitHub pull requests are the preferred method to contribute code to dotCMS. We welcome contributions to the dotCMS UVE SDK! If you'd like to contribute, please follow these steps:
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)Please ensure your code follows the existing style and includes appropriate tests.
dotCMS comes in multiple editions and as such is dual-licensed. The dotCMS Community Edition is licensed under the GPL 3.0 and is freely available for download, customization, and deployment for use within organizations of all stripes. dotCMS Enterprise Editions (EE) adds several enterprise features and is available via a supported, indemnified commercial license from dotCMS. For the differences between the editions, see the feature page.
This SDK is part of dotCMS's dual-licensed platform (GPL 3.0 for Community, commercial license for Enterprise).
Learn more at dotcms.com.
FAQs
Official JavaScript library for interacting with Universal Visual Editor (UVE)
The npm package @dotcms/uve receives a total of 381 weekly downloads. As such, @dotcms/uve popularity was classified as not popular.
We found that @dotcms/uve demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Node.js patched a crash bug where AsyncLocalStorage could cause stack overflows to bypass error handlers and terminate production servers.

Research
/Security News
A malicious Chrome extension steals newly created MEXC API keys, exfiltrates them to Telegram, and enables full account takeover with trading and withdrawal rights.

Security News
CVE disclosures hit a record 48,185 in 2025, driven largely by vulnerabilities in third-party WordPress plugins.