@dotcms/vue
v1.5.5
Published
Official Vue components library to render a dotCMS page.
Readme
dotCMS Vue SDK
The @dotcms/vue SDK is the dotCMS official Vue 3 library. It empowers Vue developers to build powerful, editable websites and applications in no time, with full support for the Universal Visual Editor (UVE).
Table of Contents
- Prerequisites & Setup
- Quickstart: Render a Page with dotCMS
- SDK Reference
- Troubleshooting
- Support
- Contributing
- Licensing
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:
- ☁️ Cloud hosting options - managed solutions with SLA
- 🛠️ Self-hosted options - deploy on your infrastructure
For Testing & Development:
- 🧑🏻💻 dotCMS demo site - perfect for trying out the SDK
- 📘 Learn how to use the demo site
- 📝 Read-only access, ideal for building proof-of-concepts
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!
When configuring the UVE app, point it at your Vue app's URL (e.g. http://localhost:5173):
{ "config": [{ "pattern": "(.*)", "url": "http://localhost:5173" }] }Create a dotCMS API Key
[!TIP] Make sure your API Token has read-only permissions for Pages, Folders, Assets, and Content. Using a key with minimal permissions follows security best practices.
This integration requires an API Key with read-only permissions for security best practices:
- Go to the dotCMS admin panel.
- Click on System > Users.
- Select the user you want to create the API Key for.
- Go to API Access Key and generate a new key.
For detailed instructions, please refer to the dotCMS API Documentation - Read-only token.
Installation
npm install @dotcms/vue@latestRequires Vue 3.4+ (declared as a peer dependency). The install also brings in:
@dotcms/uve: Enables interaction with the Universal Visual Editor for real-time content editing@dotcms/client: Provides the core client functionality for fetching and managing dotCMS data@tinymce/tinymce-vue: Powers inline text editing inDotCMSEditableText
dotCMS Client Configuration
import { createDotCMSClient } from '@dotcms/client';
export const dotCMSClient = createDotCMSClient({
dotcmsUrl: import.meta.env.VITE_DOTCMS_HOST,
authToken: import.meta.env.VITE_DOTCMS_AUTH_TOKEN, // Optional for public content
siteId: import.meta.env.VITE_DOTCMS_SITE_ID, // Optional site identifier/name
requestOptions: {
// The UVE needs fresh data so in-context edits are reflected immediately.
cache: 'no-cache'
}
});Proxy Configuration for Static Assets
Configure a proxy to leverage the powerful dotCMS image API, allowing you to resize and serve optimized images efficiently. This enhances application performance and improves the user experience.
1. Configure Vite
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
server: {
proxy: {
'/dA': {
target: 'https://your-dotcms-instance.com',
changeOrigin: true
}
}
}
});Learn more about Vite configuration here.
2. Usage in Components
Once configured, image URLs in your components will automatically be proxied to your dotCMS instance:
📚 Learn more about Image Resizing and Processing in dotCMS.
<script setup lang="ts">
import type { DotCMSBasicContentlet } from '@dotcms/types';
defineProps<{ contentlet: DotCMSBasicContentlet }>();
</script>
<template>
<img :src="`/dA/${contentlet.inode}`" :alt="contentlet.title" />
</template>Quickstart: Render a Page with dotCMS
The following example shows how to quickly set up a basic dotCMS page renderer in your Vue application. It demonstrates how to:
- Fetch a dotCMS page and render it with
DotCMSLayoutBody - Map content types to your own Vue components
- Keep the page editable and live-updating inside the Universal Visual Editor
Because a Vue composable must be called synchronously in setup, the recommended pattern splits fetching from rendering: a fetcher loads the page, then mounts a renderer child that receives the resolved page and calls useEditableDotCMSPage.
<!-- PageView.vue — fetches the page, then mounts the renderer -->
<script setup lang="ts">
import { shallowRef, onMounted } from 'vue';
import PageRenderer from './PageRenderer.vue';
import { dotCMSClient } from './dotCMSClient';
// shallowRef keeps the response a plain object (see toPlain / reactivity note below).
const pageResponse = shallowRef<Awaited<ReturnType<typeof dotCMSClient.page.get>> | null>(null);
onMounted(async () => {
pageResponse.value = await dotCMSClient.page.get('/');
});
</script>
<template>
<PageRenderer v-if="pageResponse" :page-response="pageResponse" />
</template><!-- PageRenderer.vue — renders the resolved page -->
<script setup lang="ts">
import { DotCMSLayoutBody, useEditableDotCMSPage } from '@dotcms/vue';
import { computed } from 'vue';
import Blog from './content-types/Blog.vue';
import Product from './content-types/Product.vue';
const props = defineProps<{ pageResponse: unknown }>();
const pageComponents = {
Blog,
Product
};
// Returns a reactive ref that live-updates while editing in the UVE.
const page = useEditableDotCMSPage(props.pageResponse as never);
const pageAsset = computed(() => page.value?.pageAsset);
</script>
<template>
<DotCMSLayoutBody v-if="pageAsset" :page="pageAsset" :components="pageComponents" />
</template>Example Project 🚀
Looking to get started quickly? Our Vue.js starter project is the perfect launchpad. This Vite + TypeScript + Tailwind template demonstrates everything you need:
📦 Fetch and render dotCMS pages with best practices 🧩 Map components to different content types 🔍 Listing pages with search functionality 📝 Detail pages with the block editor 📈 Image and asset optimization for better performance ✨ Seamless editing via the Universal Visual Editor (UVE) ⚡️ Vue Composition API + reactive live updates
SDK Reference
All components, composables and utilities are imported from @dotcms/vue.
DotCMSLayoutBody
DotCMSLayoutBody renders the layout for a dotCMS page (rows → columns → containers → contentlets), dispatching each contentlet to the mapped component. It supports both production and development modes.
| Prop | Type | Required | Default | Description |
| ------------ | -------------------------- | -------- | -------------- | -------------------------------------------------------------------- |
| page | DotCMSPageAsset | ✅ | - | The page asset containing the layout to render |
| components | Record<string, Component>| ✅ | {} | Map of content type → Vue component |
| mode | DotCMSPageRendererMode | ❌ | 'production' | Rendering mode ('production' or 'development') |
Usage
<script setup lang="ts">
import { DotCMSLayoutBody } from '@dotcms/vue';
import type { DotCMSPageAsset } from '@dotcms/types';
import Blog from './content-types/Blog.vue';
import Product from './content-types/Product.vue';
defineProps<{ page: DotCMSPageAsset }>();
const components = {
Blog,
Product
};
</script>
<template>
<DotCMSLayoutBody :page="page" :components="components" />
</template>Layout Body Modes
production: Performance-optimized mode that only renders content with explicitly mapped components, leaving unmapped content empty.development: Debug-friendly mode that renders a fallback for unmapped content types and shows visual indicators for empty containers and missing mappings. Inside the UVE, edit mode is detected automatically — the editordata-dot-*metadata is always emitted regardless of themodeprop.
Component Mapping
The components prop maps content type variable names to Vue components. Each contentlet's fields are passed to the matched component as props.
const components = {
Blog: MyBlogCard,
Product: MyProductCard,
CustomNoComponent: MyFallback // optional: rendered for unmapped types in dev mode
};- Keys (e.g.
Blog,Product): must match your content type variable names in dotCMS (they are case-sensitive — e.g.webPageContent,calendarEvent). - Values: the Vue component that renders that content type. Declare a typed props interface for the fields you use — do not spread the full
DotCMSBasicContentlettype onto a component, as Vue runtime-validates every declared prop and dotCMS ships some fields (e.g.language,modDate) with types that differ from the type defs. - The special
CustomNoComponentkey is the fallback rendered when no mapping exists.
[!TIP] Always use the exact content type variable name from dotCMS as the key. You can find it in the Content Types section of your dotCMS admin panel.
DotCMSEditableText
DotCMSEditableText enables inline editing of a single text field in dotCMS. Inside the UVE in edit mode it mounts a TinyMCE editor; everywhere else it renders the field's current value.
| Prop | Type | Required | Default | Description |
| ------------ | ----------------------------- | -------- | --------- | ------------------------------------------------------------------ |
| contentlet | T extends DotCMSBasicContentlet | ✅ | - | The contentlet containing the editable field |
| fieldName | keyof T | ✅ | - | Name of the field to edit (must be a valid key of the contentlet) |
| mode | 'plain' \| 'minimal' \| 'full' | ❌ | 'plain' | TinyMCE toolbar preset. full enables the full style bubble menu. |
| format | 'text' \| 'html' | ❌ | 'text' | text renders HTML as plain text; html interprets HTML markup |
Usage
<script setup lang="ts">
import { DotCMSEditableText } from '@dotcms/vue';
import type { DotCMSBasicContentlet } from '@dotcms/types';
const props = defineProps<{ contentlet: DotCMSBasicContentlet }>();
</script>
<template>
<div class="banner">
<img :src="`/dA/${contentlet.inode}`" :alt="contentlet.title" />
<h2>
<DotCMSEditableText :contentlet="contentlet" field-name="title" />
</h2>
</div>
</template>Editor Integration
- Detects UVE edit mode and enables inline TinyMCE editing.
- Sends the edited content back to the editor on blur, without opening the full content dialog.
- The TinyMCE script is loaded from your dotCMS instance (
dotCMSHost), so no extra TinyMCE install is required.
DotCMSBlockEditorRenderer
DotCMSBlockEditorRenderer renders Block Editor content from dotCMS, with support for custom block renderers.
| Prop | Type | Required | Default | Description |
| ----------------- | --------------------------- | -------- | ------- | --------------------------------------------------------------- |
| blocks | BlockEditorNode | ✅ | - | The Block Editor field value to render |
| customRenderers | CustomRenderer | ❌ | - | Custom Vue components for specific block types / content types |
| className | string | ❌ | - | CSS class applied to the container |
| style | CSSProperties | ❌ | - | Inline styles for the container |
| isDevMode | boolean | ❌ | false | When true, shows a visible message for invalid/unknown blocks |
Usage
A custom renderer is a Vue component that receives the block as a node prop and the rendered children in its default <slot />.
<!-- CustomHeading.vue -->
<script setup lang="ts">
import type { CustomRendererProps } from '@dotcms/vue';
defineProps<CustomRendererProps>();
</script>
<template>
<h1 class="my-heading"><slot /></h1>
</template><!-- DetailPage.vue -->
<script setup lang="ts">
import { DotCMSBlockEditorRenderer, type CustomRenderer } from '@dotcms/vue';
import type { DotCMSBasicContentlet } from '@dotcms/types';
import CustomHeading from './CustomHeading.vue';
const props = defineProps<{ contentlet: DotCMSBasicContentlet }>();
const customRenderers: CustomRenderer = {
heading: CustomHeading
};
</script>
<template>
<DotCMSBlockEditorRenderer
:blocks="contentlet.myBlockEditorField"
:custom-renderers="customRenderers" />
</template>Recommendations
- Should not be used together with
DotCMSEditableTexton the same field. - Be mindful that the CSS cascade can affect the look and feel of your blocks.
- Only works with Block Editor fields. For other text fields, use
DotCMSEditableText.
DotCMSShow
DotCMSShow conditionally renders its slot content based on the current UVE mode — useful for editor-only affordances like Edit or Reorder buttons.
| Prop | Type | Required | Default | Description |
| ------ | ---------- | -------- | ---------------- | -------------------------------------------------------- |
| when | UVE_MODE | ❌ | UVE_MODE.EDIT | The UVE mode in which the slot content should be shown |
Usage
<script setup lang="ts">
import { DotCMSShow } from '@dotcms/vue';
import { UVE_MODE } from '@dotcms/types';
</script>
<template>
<DotCMSShow :when="UVE_MODE.EDIT">
<button>Edit</button>
</DotCMSShow>
</template>📚 Learn more about the UVE_MODE enum in the dotCMS UVE documentation.
useEditableDotCMSPage
useEditableDotCMSPage wires a page into the Universal Visual Editor and returns a reactive ref that live-updates as an editor makes changes. Outside the UVE it is a pass-through — the returned ref simply holds the initial response.
| Param | Type | Required | Description |
| -------------- | ----------------------------- | -------- | --------------------------------------------- |
| pageResponse | DotCMSComposedPageResponse | ✅ | The page response from client.page.get() |
Returns a Ref<DotCMSComposedPageResponse> — access .value.pageAsset and .value.content.
When you use the composable, it:
- Initializes the UVE with your page data
- Keeps the editor navigation in sync
- Subscribes to content changes and swaps in the new page automatically when content is edited, blocks are added/removed, layout changes, or components are moved
- Cleans up all listeners on unmount
Usage
<script setup lang="ts">
import { DotCMSLayoutBody, useEditableDotCMSPage } from '@dotcms/vue';
import { computed } from 'vue';
const props = defineProps<{ pageResponse: unknown }>();
const page = useEditableDotCMSPage(props.pageResponse as never);
const pageAsset = computed(() => page.value?.pageAsset);
const components = {
/* content-type → component map */
};
</script>
<template>
<DotCMSLayoutBody v-if="pageAsset" :page="pageAsset" :components="components" />
</template>[!IMPORTANT] Store the page response in a
shallowRef(not a deepref). A deeprefwraps the whole page in reactive Proxies, which the UVE cannotpostMessageto the editor (DataCloneError). SeetoPlain.
useDotCMSShowWhen
useDotCMSShowWhen returns a reactive boolean for whether the current UVE mode matches — useful for mode-based logic outside of the template.
| Param | Type | Required | Description |
| ------ | ---------- | -------- | ---------------------------------- |
| when | UVE_MODE | ✅ | The UVE mode to check against |
Usage
<script setup lang="ts">
import { useDotCMSShowWhen } from '@dotcms/vue';
import { UVE_MODE } from '@dotcms/types';
const isEditMode = useDotCMSShowWhen(UVE_MODE.EDIT); // Readonly<Ref<boolean>>
</script>
<template>
<button v-if="isEditMode">Edit</button>
</template>toPlain
toPlain deep-unwraps a Vue reactive value (refs / reactive proxies) into a plain, structured-clone-safe object. Use it before passing reactive data to UVE editor actions (editContentlet, enableBlockEditorInline, …) from @dotcms/uve, which send their argument to the editor via postMessage.
<script setup lang="ts">
import { toPlain } from '@dotcms/vue';
import { editContentlet } from '@dotcms/uve';
import type { DotCMSBasicContentlet } from '@dotcms/types';
const props = defineProps<{ contentlet: DotCMSBasicContentlet }>();
const onEdit = () => editContentlet(toPlain(props.contentlet));
</script>Troubleshooting
Common Issues & Solutions
Universal Visual Editor (UVE)
UVE not loading: the page renders but the editor tools/overlays don't appear.
- Possible causes: incorrect UVE app configuration; missing
useEditableDotCMSPage; the app is not running inside the UVE iframe. - Solutions: verify the UVE app URL matches your Vue dev server; ensure the page tree is wrapped with
useEditableDotCMSPage; confirmdata-dot-object="contentlet"attributes are present on rendered contentlets (they only appear in edit mode).
- Possible causes: incorrect UVE app configuration; missing
DataCloneError: could not be clonedwhen loading in the editor.- Cause: a Vue reactive Proxy was sent to the editor via
postMessage. - Solutions: store the page response in a
shallowRef, not a deepref; usetoPlainbefore calling UVE editor actions with reactive data.
- Cause: a Vue reactive Proxy was sent to the editor via
Content edits don't update the page live.
- Cause: usually a duplicate Vue instance — if
vueis bundled into a consumer copy, the SDK's reactivity is a different instance from your app's. Ensure a singlevueis installed.
- Cause: usually a duplicate Vue instance — if
Missing Content
Components not rendering: empty spaces where content should appear.
- Solutions: check the
componentsmap registration; verify content type variable names match exactly (case-sensitive); usemode="development"for detailed logging.
- Solutions: check the
Prop validation warnings (e.g. Invalid prop: type check failed for prop "modDate").
- Cause: a content-type component declares props by extending the full contentlet type; dotCMS ships some system fields with types that differ from the defs.
- Solution: declare only the specific fields your component reads.
Development Setup
npm installfails: clear the npm cache (npm cache clean --force), deletenode_modules, and reinstall; verify your Node.js version.Runtime errors about missing imports: check that all SDK imports come from
@dotcms/vue, and that peer dependencies (vue,@dotcms/client,@dotcms/uve,@dotcms/types) are installed.
Debugging Tips
Enable development mode
<DotCMSLayoutBody :page="pageAsset" :components="components" mode="development" />This shows detailed messages, renders fallbacks for unmapped components, and highlights empty containers.
Check the browser console and network tab for errors, and watch for 401/403 (auth) responses.
Inspect the DOM inside the UVE iframe — contentlets should carry
data-dot-object="contentlet"and containersdata-dot-object="container"in edit mode.
Still Having Issues?
If you're still experiencing problems after trying these solutions:
- Search existing GitHub issues
- Ask questions on the community forum
- Create a new issue with detailed reproduction steps, environment information, error messages, and code samples
Support
We offer multiple channels to get help with the dotCMS Vue SDK:
- GitHub Issues: For bug reports and feature requests, please open an issue.
- Community Forum: Join our community discussions.
- Enterprise Support: Enterprise customers can access premium support through the dotCMS Support Portal.
When reporting issues, please include:
- SDK version you're using
- Vue 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 Vue SDK! If you'd like to contribute, please:
- Fork the repository dotCMS/core
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - 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.
