npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dotcms/uve

v26.7.27-1

Published

Official JavaScript library for interacting with Universal Visual Editor (UVE)

Readme

dotCMS UVE SDK

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, rendering, and event wiring for you.

With @dotcms/uve, framework SDKs are able to:

  • Make pages and contentlets editable
  • Respond to editor events (content updates, mode changes)
  • Trigger modal or inline editing experiences
  • Sync app routing with the dotCMS editor

Table of Contents

Before You Use @dotcms/uve

Getting Started: Recommended Examples

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.

🚩 Custom Setup: Manual Rendering (Not Recommended)

💡 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:

  1. You must use @dotcms/client to fetch content and page data.
  2. You must render pages based on dotCMS’s layout schema.
  3. You must apply the correct data-dot-* attributes to containers and contentlets.

Here's a minimal setup using @dotcms/client and @dotcms/uve:

  1. Initialize the Client and get the page response:
// 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;
};
  1. Initialize the UVE and subscribe to changes:

⚠️ The initUVE() function only works with a PageResponse returned 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 { UVEEventType } from '@dotcms/types';
import { getPage } from './getPage';

const pageResponse = await getPage();

initUVE(pageResponse);
createUVESubscription(UVEEventType.CONTENT_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.

  1. Create a custom render for the page:
// 🔧 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.

🔄 How to Render a dotCMS Page

📚 For a complete guide, here is a full tutorial: 👉 dotCMS Page Rendering Architecture

dotCMS pages are structured as nested layout objects:

  • A PageAsset contains a layout object
  • The layout includes rows, columns, containers, and contentlets

Here’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.

Prerequisites & Setup

Get a dotCMS Environment

Version Compatibility

  • Recommended: dotCMS Evergreen
  • Minimum: dotCMS v25.05
  • Best Experience: Latest Evergreen release

Environment Setup

For Production Use:

For Testing & Development:

For Local Development:

Configure The Universal Visual Editor App

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!

Installation

npm install @dotcms/uve@latest

Using the SDK with TypeScript

All interfaces and types are available through the @dotcms/types package:

npm install @dotcms/types@latest --save-dev

Common Types

The SDK uses several key types from @dotcms/types:

import {
    DotCMSBasicContentlet,
    DotCMSPageResponse,
    DotCMSInlineEditingType,
    UVEEventType,
    UVEState
} from '@dotcms/types';

For a complete reference of all available types and interfaces, please refer to the @dotcms/types documentation.

SDK Reference

initUVE(config?: DotCMSPageResponse)

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 |

Usage

const { destroyUVESubscriptions } = initUVE(pageResponse);

⚠️ If you don't provide a pageResponse, we cannot guarantee that the UVE will be initialized correctly.

getUVEState()

getUVEState is a function that returns the UVE state if UVE is active.

Usage

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;
};

UVE State

  • dotCMSHost: The host URL of the DotCMS instance
  • experimentId: The ID of the current experiment
  • languageId: The language ID of the current page set on the UVE
  • mode: The current editor mode ('preview', 'edit', 'live')
  • persona: The persona of the current page set on the UVE
  • publishDate: The publish date of the current page set on the UVE
  • variantName: The name of the current variant

createUVESubscription(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 |

Usage

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();

Event Types

  • UVEEventType.CONTENT_CHANGES: Triggered when the content of the page changes.
  • UVEEventType.PAGE_RELOAD: Triggered when the page is reloaded.
  • UVEEventType.IFRAME_SCROLL: Triggered when scroll action is needed inside the iframe ('up' or 'down').
  • UVEEventType.CONTENTLET_HOVERED: Triggered when a contentlet is hovered.
  • UVEEventType.CONTENTLET_CLICKED: Triggered when a contentlet is clicked.
  • UVEEventType.AUTO_BOUNDS: Triggered when the SDK syncs page bounds after layout changes (internal SDK use).
  • UVEEventType.SCROLL_TO_SECTION: Triggered when the editor requests a scroll to a specific page section (internal).
  • UVEEventType.SELECTION_CLEARED: Triggered when the editor clears its selection (internal).

editContentlet(contentlet)

editContentlet is a function that opens the dotCMS modal editor for any contentlet in or out of the page area.

| Input | Type | Required | Description | | ------------ | ------------------ | -------- | -------------------------------- | | contentlet | Contentlet<T> | ✅ | The contentlet you want to edit. |

Usage

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' | | data | DotCMSInlineEditingPayload | ✅ | Field content required to enable inline editing |

Usage

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>
  );
};

DotCMSInlineEditingPayload

  • 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 |

Usage

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 |

Usage

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 |

Usage

import { reorderMenu } from '@dotcms/uve';

reorderMenu({ startLevel: 2, depth: 3 });

DotCMSReorderMenuConfig

  • startLevel (number): The level to start reordering from
  • depth (number): The depth of the menu to reorder

sendMessageToUVE(message)

sendMessageToUVE is a low-level function to send custom messages to UVE.

| Input | Type | Required | Description | | --------- | ------------------------------------------ | -------- | ---------------------------- | | message | DotCMSUVEMessage<T> | ✅ | Object with action + payload |

Usage

sendMessageToUVE({
  action: DotCMSUVEAction.CUSTOM_EVENT,
  payload: { type: 'MyEvent', data: {...} }
});

DotCMSUVEMessage

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

Style Editor

What is the Style Editor?

The Style Editor is a powerful feature that enables content authors to customize component appearance, layout, typography, colors, and other configurable aspects in real time within the Universal Visual Editor (UVE), without requiring code changes or page reloads.

Style editor schemas are configured in the DotCMS admin UI under Content Types (in the content type metadata). The SDK fetches schemas automatically — no schema definition code is required in your application.

Key Benefits:

  • Real-Time Visual Editing: Modify component styles and see changes instantly in the editor
  • Content-Specific Customization: Different content types can have unique style schemas, and the same contentlet could have different styles depending on whether it is located in a different container or page
  • Admin-Managed: Style schemas are defined in the DotCMS admin UI under Content Types and fetched automatically by the SDK

Use Cases:

  • Adjust typography (font size, family, weight)
  • Configure layouts (grid columns, alignment, spacing)
  • Customize colors and themes
  • Toggle component features (borders, shadows, decorations)
  • Control responsive behavior
  • Modify animation settings

Accessing Style Values

Style Editor values are managed internally by UVE and passed to your components through the dotStyleProperties attribute. This attribute is available in your contentlet component props.

In React Components

When rendering contentlets, style properties are accessed through the dotStyleProperties prop:

import { DotCMSBasicContentlet } from '@dotcms/types';

interface ActivityProps {
    contentlet: DotCMSBasicContentlet;
    dotStyleProperties?: Record<string, any>;
}

function Activity(props: ActivityProps) {
    const { title, description, dotStyleProperties } = props; // Contentlet information

    // Access style values using dot notation or bracket notation
    const fontSize = dotStyleProperties?.['font-size'];
    const textAlign = dotStyleProperties?.text;
    const layout = dotStyleProperties?.layout;

    return (
        <div style={{ fontSize, textAlign }}>
            <h1>{title}</h1>
            <p>{description}</p>
        </div>
    );
}

Value Types by Field Type

Input Field:

// Returns: string (text) or number (number input)
const fontSize: string = '16px';
const padding: number = 24;

Dropdown Field:

// Returns: string (the selected value)
const theme: string = 'light';
const fontFamily: string = 'Arial';

Radio Field:

// Returns: string (the selected value)
const layout: string = 'left';
const alignment: string = 'center';

Checkbox Group:

// Returns: Record<string, boolean> (object with key-value pairs)
const textStyles: Record<string, boolean> = {
    bold: true,
    italic: false,
    underline: true,
    strikethrough: false
};

// Access individual values
if (textStyles.bold) {
    // Apply bold styling
}

Applying Style Values

Use the style values to conditionally render styles, classes, or component variants:

function BlogPost(props) {
    const { title, body, dotStyleProperties } = props;

    // Example: Apply dynamic font size
    const fontSize = dotStyleProperties?.['font-size'] || '16px';

    // Example: Apply layout classes
    const layout = dotStyleProperties?.layout || 'default';
    const layoutClass = `layout-${layout}`;

    // Example: Apply checkbox group values
    const textStyles = dotStyleProperties?.['text-style'] || {};
    const textStyleClasses = [
        textStyles.bold ? 'font-bold' : '',
        textStyles.italic ? 'font-italic' : '',
        textStyles.underline ? 'text-underline' : ''
    ]
        .filter(Boolean)
        .join(' ');

    return (
        <div className={`${layoutClass} ${textStyleClasses}`} style={{ fontSize }}>
            <h1>{title}</h1>
            <p>{body}</p>
        </div>
    );
}

💡 Note: The dotStyleProperties prop is automatically passed to your contentlet components by the framework SDK when UVE is active.

Current Capabilities and Limitations:

When defining styles for a contentlet within a page using Style Editor, the following behaviors might occur:

| Scenario | Behavior | Result | |---------------------------------------------------|-----------------------------------------------------------------------------------|---------------------------------------------| | Same Contentlet, Different Containers, Same Page | Page A: { Container_1: contentlet_1, Container_2: contentlet_1 } | 🎨 Styles are different | | Same Contentlet, Same Container, Different Pages | Page A: { Container_1: contentlet_1 }, Page B: { Container_1: contentlet_1 } | 🎨 Styles are different | | Copying a Page with Styled Content | Creating Page B as a copy of Page A, where Page A includes styled content | ✅ Styles preserved, 🎨 Styles are different | | Moving Styled Content to Same Container Type | system-container → system-container | ✅ Styles preserved | | Moving Styled Content to Different Container Type | system-container → custom-container | ⚠️ Styles lost | | Adding, Deleting, or Moving Unstyled Content | Performing any structural change on the page that does not involve styled content | if any: ✅ Styles preserved |

NOTE: (🎨 Styles are different) means the capability to define distinct styles, even when utilizing the identical Contentlet.

The only known limitation is that moving a contentlet with defined styles between different container types (5th scenario) results in the loss of those styles. A fix for this scenario is on our roadmap.

Troubleshooting

Common Issues & Solutions

Memory Management

  1. Memory Leaks: Application experiences memory leaks
    • Possible Causes:
      • Failing to call destroyUVESubscriptions() on unmount
    • Solutions:
      • Always call destroyUVESubscriptions() when your component unmounts to clean up subscriptions

Editor State

  1. Undefined State: getUVEState() returns undefined
    • Possible Causes:
      • Application not running inside the dotCMS editor
    • Solutions:
      • Ensure your application is running within the dotCMS environment when calling getUVEState()

Event Handling

  1. Unsubscribed Events: Events not unsubscribed leading to unexpected behavior
    • Possible Causes:
      • Not unsubscribing from events
    • Solutions:
      • Always unsubscribe from events using the unsubscribe() method to prevent memory leaks

Inline Editing

  1. Invalid Contentlet or Field: initInlineEditing() requires valid contentlet and field name
    • Possible Causes:
      • Incorrect contentlet or field name
    • Solutions:
      • Verify that the contentlet and field name are correct and exist in the dotCMS instance

Non-existent Page Navigation

  1. Navigation to a non-existent page: May break content sync in UVE and make the editor redirect to the home page
    • Possible Causes:
      • Navigation to a non-existent page
    • Solutions:
      • Ensure the page exists in the dotCMS instance

Menu Reordering

  1. UI Action Requirement: reorderMenu() must be called from a UI action
    • Possible Causes:
      • Attempting to auto-trigger reorderMenu()
    • Solutions:
      • Ensure reorderMenu() is triggered by a user action within the UI

Debugging Tips

  1. Ensure you are in the UVE Context
    • Check if you are in the UVE context by calling getUVEState()
    • If you are not in the UVE context, you will not be able to use the UVE SDK correctly
  2. Check Browser Console
    • Check for errors in the browser console
    • Check for errors in the browser network tab
  3. Network Monitoring
    • Use browser dev tools to monitor API calls
    • Check for 401/403 errors (auth issues)
    • Verify asset loading paths

Still Having Issues?

If you're still experiencing problems after trying these solutions:

  1. Search existing GitHub issues
  2. Check our community forum
  3. Create a new issue with:
    • Detailed reproduction steps
    • Environment information
    • Error messages
    • Code samples

Support

We offer multiple channels to get help with the dotCMS UVE SDK:

  • GitHub Issues: For bug reports and feature requests, please open an issue in the GitHub repository
  • Community Forum: Join our community discussions to ask questions and share solutions
  • Stack Overflow: Use the tag dotcms-uve when posting questions
  • Enterprise Support: Enterprise customers can access premium support through the dotCMS Support Portal

When reporting issues, please include:

  • SDK version you're using
  • dotCMS version
  • Minimal reproduction steps
  • Expected vs. actual behavior

Contributing

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:

  1. Fork the repository dotCMS/core
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Please ensure your code follows the existing style and includes appropriate tests.

Licensing

dotCMS is available under either the Business Source License 1.1 (BSL) or a commercial license.

Under the BSL, dotCMS can be used at no cost by individual developers, small businesses or agencies under $5M in total finances, and by larger organizations in non-production environments. Every BSL release automatically converts to GPL v3 four years after its release date. For full terms and FAQs, visit dotcms.com/bsl and dotcms.com/bsl-faq.

Production use in larger organizations, along with access to managed cloud, SLAs, support, and enterprise capabilities, is available under a commercial license from dotCMS. For details on commercial plans, features, and support options, see dotcms.com/pricing.