The @dotcms/react SDK is the DotCMS official React library. It empowers React developers to build powerful, editable websites and applications in no time.
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!
[!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:
For detailed instructions, please refer to the dotCMS API Documentation - Read-only token.
npm install @dotcms/react@latestThis will automatically install the required dependencies:
@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 dataimport { createDotCMSClient } from '@dotcms/client';
type DotCMSClient = ReturnType<typeof createDotCMSClient>;
export const dotCMSClient: DotCMSClient = createDotCMSClient({
dotcmsUrl: 'https://your-dotcms-instance.com',
authToken: 'your-auth-token', // Optional for public content
siteId: 'your-site-id' // Optional site identifier/name
});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 user experience, making it a strategic enhancement for your project.
// vite.config.ts
import { defineConfig } from 'vite';
import dns from 'node:dns';
dns.setDefaultResultOrder('verbatim');
export default defineConfig({
server: {
proxy: {
'/dA': {
target: 'your-dotcms-instance.com',
changeOrigin: true
}
}
}
});Learn more about Vite configuration here.
Once configured, image URLs in your components will automatically be proxied to your dotCMS instance:
📚 Learn more about Image Resizing and Processing in dotCMS with Angular and Next.js.
// /components/my-dotcms-image.tsx
import type { DotCMSBasicContentlet } from '@dotcms/types';
export const MyDotCMSImageComponent = ({ inode, title }: DotCMSBasicContentlet) => {
return <img src={`/dA/${inode}`} alt={title} />;
}The following example demonstrates how to quickly set up a basic dotCMS page renderer in your React application. This example shows how to:
// /src/app/pages/dotcms-page.tsx
import { useState, useEffect } from 'react';
import { DotCMSLayoutBody, useEditableDotCMSPage } from '@dotcms/react';
import { DotCMSPageResponse } from '@dotcms/types';
import { dotCMSClient } from './dotCMSClient';
import { BlogComponent } from './BlogComponent';
import { ProductComponent } from './ProductComponent';
const COMPONENTS_MAP = {
Blog: BlogComponent,
Product: ProductComponent
};
const MyPage = () => {
const [response, setResponse] = useState<DotCMSPageResponse | null>(null);
const { pageAsset } = useEditableDotCMSPage(response);
useEffect(() => {
dotCMSClient.page.get('/').then((response) => {
setResponse(response);
});
}, []);
return <DotCMSLayoutBody page={pageAsset} components={COMPONENTS_MAP} mode="development" />;
};
export default MyPage;Looking to get started quickly? We've got you covered! Our Next.js starter project is the perfect launchpad for your dotCMS + Next.js journey. This production-ready template demonstrates everything you need:
📦 Fetch and render dotCMS pages with best practices 🧩 Register and manage components for different content types 🔍 Listing pages with search functionality 📝 Detail pages for blogs 📈 Image and assets optimization for better performance ✨ Enable seamless editing via the Universal Visual Editor (UVE) ⚡️ Leverage React's hooks and state management for optimal performance
[!TIP] This starter project is more than just an example, it follows all our best practices. We highly recommend using it as the base for your next dotCMS + Next.js project!
All components and hooks should be imported from @dotcms/react:
DotCMSLayoutBody is a component used to render the layout for a DotCMS page, supporting both production and development modes.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
page | DotCMSPageAsset | ✅ | - | The page asset containing the layout to render |
components | DotCMSPageComponent | ✅ | {} | Map of content type → React component |
mode | DotCMSPageRendererMode | ❌ | 'production' | Rendering mode ('production' or 'development') |
slots | Record<string, ReactNode> | ❌ | {} | Pre-rendered server component nodes keyed by contentlet identifier. See buildSlots. |
DotCMSLayoutBody requires a "use client" parent because components is a map of React component functions, which React cannot serialize across the server→client boundary. The recommended pattern is to fetch data in the server page and pass the plain result to a client wrapper:
// page.tsx — server component, fetches data only
export default async function Page() {
const pageContent = await getDotCMSPage(path);
return <PageView pageContent={pageContent} />;
}// PageView.tsx — "use client", owns components and renders layout
'use client';
export function PageView({ pageContent }) {
const { pageAsset } = useEditableDotCMSPage(pageContent);
return <DotCMSLayoutBody page={pageAsset} components={pageComponents} />;
}import type { DotCMSPageResponse } from '@dotcms/types';
import { DotCMSLayoutBody } from '@dotcms/react';
import { MyBlogCard } from './MyBlogCard';
import { DotCMSProductComponent } from './DotCMSProductComponent';
const COMPONENTS_MAP = {
Blog: MyBlogCard,
Product: DotCMSProductComponent
};
const MyPage = ({ pageAsset }: DotCMSPageResponse) => {
return <DotCMSLayoutBody page={pageAsset} components={COMPONENTS_MAP} />;
};Use buildSlots when you have Next.js async server components that need to fetch their own data. Since async server components can't be called from inside a client component tree, pre-render them on the server and pass the result into the layout via slots:
import { buildSlots, DotCMSLayoutBody } from '@dotcms/react';
// BlogListContainer is an async server component that fetches its own data
const slots = buildSlots(pageContent.pageAsset.containers, {
BlogList: BlogListContainer,
});
<DotCMSLayoutBody page={pageAsset} components={pageComponents} slots={slots} />The layout renders the pre-rendered slot node for a contentlet if one exists, otherwise falls back to the matching entry in components.
production: Performance-optimized mode that only renders content with explicitly mapped components, leaving unmapped content empty.development: Debug-friendly mode that renders default components for unmapped content types and provides visual indicators and console logs for empty containers and missing mappings.The DotCMSLayoutBody component uses a components prop to map content type variable names to React components. This allows you to render different components for different content types. Example:
const DYNAMIC_COMPONENTS = {
Blog: MyBlogCard,
Product: DotCMSProductComponent
};Blog, Product): Match your content type variable names in dotCMS[!TIP] Always use the exact content type variable name from dotCMS as the key. You can find this in the Content Types section of your dotCMS admin panel.
DotCMSEditableText is a component for inline editing of text fields in dotCMS, supporting plain text, text area, and WYSIWYG fields.
| Input | Type | Required | Description |
|---|---|---|---|
contentlet | T extends DotCMSBasicContentlet | ✅ | The contentlet containing the editable field |
fieldName | keyof T | ✅ | Name of the field to edit, which must be a valid key of the contentlet type T |
mode | 'plain' | 'full' | ❌ | plain (default): Supports text editing. Does not show style controls. full: Enables a bubble menu with style options. This mode only works with WYSIWYG fields. |
format | 'text' | 'html' | ❌ | text (default): Renders HTML tags as plain text html: Interprets and renders HTML markup |
import type { DotCMSBasicContentlet } from '@dotcms/types';
import { DotCMSEditableText } from '@dotcms/react';
const MyBannerComponent = ({ contentlet }: { contentlet: DotCMSBasicContentlet }) => {
const { inode, title, link } = contentlet;
return (
<div className="flex overflow-hidden relative justify-center items-center w-full h-96 bg-gray-200">
<img className="object-cover w-full" src={`/dA/${inode}`} alt={title} />
<div className="flex absolute inset-0 flex-col justify-center items-center p-4 text-center text-white">
<h2 className="mb-2 text-6xl font-bold text-shadow">
<DotCMSEditableText fieldName="title" contentlet={contentlet} />
</h2>
<a
href={link}
className="p-4 text-xl bg-red-400 rounded-sm transition duration-300 hover:bg-red-500">
See more
</a>
</div>
</div>
);
};
export default MyBannerComponent;Save workflow action on blur without needing full content dialog.DotCMSBlockEditorRenderer is a component for rendering Block Editor content from dotCMS with support for custom block renderers.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
blocks | BlockEditorContent | ✅ | - | The Block Editor content to render |
customRenderers | CustomRenderers | ❌ | - | Custom rendering functions for specific block types |
className | string | ❌ | - | CSS class to apply to the container |
style | CSSProperties | ❌ | - | Inline styles for the container |
isDevMode | boolean | ❌ | false | When true, shows a visible error message if the blocks data is invalid. When false (default), invalid blocks render nothing silently. |
import type { DotCMSBasicContentlet } from '@dotcms/types';
import { DotCMSBlockEditorRenderer } from '@dotcms/react';
import { MyCustomBannerBlock } from './MyCustomBannerBlock';
import { MyCustomH1 } from './MyCustomH1';
const CUSTOM_RENDERERS = {
customBannerBlock: MyCustomBannerBlock,
h1: MyCustomH1
};
const DetailPage = ({ contentlet }: { contentlet: DotCMSBasicContentlet }) => {
return (
<DotCMSBlockEditorRenderer
blocks={contentlet['YOUR_BLOCK_EDITOR_FIELD']}
customRenderers={CUSTOM_RENDERERS}
/>
);
};DotCMSBlockEditorRenderer can be used directly in a Next.js server component — it has no hooks or browser dependencies:
// app/article/page.tsx — server component
import { DotCMSBlockEditorRenderer } from '@dotcms/react';
export default async function ArticlePage() {
const { pageAsset } = await getDotCMSPage('/article');
const blocks = pageAsset.containers['...'].contentlets[0].blockEditorContent;
return (
<DotCMSBlockEditorRenderer
blocks={blocks}
isDevMode={process.env.NODE_ENV === 'development'}
/>
);
}DotCMSEditableTextDotCMSBlockEditorRenderer only works with Block Editor fields. For other fields, use DotCMSEditableText.📘 For advanced examples, customization options, and best practices, refer to the DotCMSBlockEditorRenderer README.
DotCMSShow is a component for conditionally rendering content based on the current UVE mode. Useful for mode-based behaviors outside of render logic.
| Input | Type | Required | Description |
|---|---|---|---|
children | ReactNode | ✅ | Content to be conditionally rendered |
when | UVE_MODE | ✅ | The UVE mode when content should be displayed: UVE_MODE.EDIT: Only visible in edit mode UVE_MODE.PREVIEW: Only visible in preview mode UVE_MODE.PUBLISHED: Only visible in published mode |
import { UVE_MODE } from '@dotcms/types';
import { DotCMSShow } from '@dotcms/react';
const MyComponent = () => {
return (
<DotCMSShow when={UVE_MODE.EDIT}>
<div>This will only render in UVE EDIT mode</div>
</DotCMSShow>
);
};📚 Learn more about the UVE_MODE enum in the dotCMS UVE Package Documentation.
useEditableDotCMSPage is a hook that enables real-time page updates when using the Universal Visual Editor.
| Param | Type | Required | Description |
|---|---|---|---|
pageResponse | DotCMSPageResponse | ✅ | The page data object from client.page.get() |
When you use the hook, it:
'use client';
import { useEditableDotCMSPage, DotCMSLayoutBody } from '@dotcms/react';
import type { DotCMSPageResponse } from '@dotcms/types';
const COMPONENTS_MAP = {
Blog: BlogComponent,
Product: ProductComponent
};
export function DotCMSPage({ pageResponse }: { pageResponse: DotCMSPageResponse }) {
const { pageAsset } = useEditableDotCMSPage(pageResponse);
return <DotCMSLayoutBody page={pageAsset} components={COMPONENTS_MAP} />;
}useDotCMSShowWhen is a hook for conditionally showing content based on the current UVE mode. Useful for mode-based behaviors outside of render logic.
| Param | Type | Required | Description |
|---|---|---|---|
when | UVE_MODE | ✅ | The UVE mode when content should be displayed: UVE_MODE.EDIT: Only visible in edit mode UVE_MODE.PREVIEW: Only visible in preview mode UVE_MODE.PUBLISHED: Only visible in published mode |
import { UVE_MODE } from '@dotcms/types';
import { useDotCMSShowWhen } from '@dotcms/react';
const MyEditButton = () => {
const isEditMode = useDotCMSShowWhen(UVE_MODE.EDIT); // returns a boolean
if (isEditMode) {
return <button>Edit</button>;
}
return null;
};useAISearch is a hook that enables AI-powered semantic search capabilities for your dotCMS content. It manages search state, handles API calls, and provides real-time status updates.
| Param | Type | Required | Description |
|---|---|---|---|
client | DotCMSClient | ✅ | The dotCMS client instance created with createDotCMSClient() |
indexName | string | ✅ | Name of the AI search index to query |
params | DotCMSAISearchParams | ✅ | Search configuration including query params (limit, offset) and AI config |
The hook returns an object with the following properties:
| Property | Type | Description |
|---|---|---|
response | DotCMSAISearchResponse<T> | null | Full search response including metadata and results |
results | DotCMSAISearchContentletData<T>[] | undefined | Array of search results with match scores |
status | DotCMSEntityStatus | Current state: IDLE, LOADING, SUCCESS, or ERROR |
search | (prompt: string) => Promise<void> | Function to execute a search with a text prompt |
reset | () => void | Function to reset search state to idle |
'use client';
import { useState } from 'react';
import { useAISearch } from '@dotcms/react';
import type { DotCMSBasicContentlet } from '@dotcms/types';
import { dotCMSClient } from '@/lib/dotCMSClient';
interface BlogPost extends DotCMSBasicContentlet {
body: string;
author: string;
}
const AISearchComponent = () => {
const [searchQuery, setSearchQuery] = useState('');
const { results, status, search, reset } = useAISearch<BlogPost>({
client: dotCMSClient,
indexName: 'blog-search-index',
params: {
query: {
limit: 10,
offset: 0,
contentType: 'Blog'
},
config: {
threshold: 0.5,
responseLength: 1024
}
}
});
const handleSearch = async () => {
if (searchQuery.trim()) {
await search(searchQuery);
}
};
const handleReset = () => {
setSearchQuery('');
reset();
};
return (
<div className="search-container">
<div className="search-bar">
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search with AI..."
/>
<button onClick={handleSearch} disabled={status.state === 'LOADING'}>
{status.state === 'LOADING' ? 'Searching...' : 'Search'}
</button>
<button onClick={handleReset}>Reset</button>
</div>
{status.state === 'ERROR' && (
<div className="error">
Error: {status.error.message}
</div>
)}
{status.state === 'SUCCESS' && results && (
<div className="results">
<h2>Found {results.length} results</h2>
{results.map((result) => (
<article key={result.identifier}>
<h3>{result.title}</h3>
<p>Author: {result.author}</p>
{result.matches?.map((match, idx) => (
<div key={idx} className="match">
<span>Relevance: {match.distance.toFixed(2)}</span>
<p>{match.extractedText}</p>
</div>
))}
</article>
))}
</div>
)}
</div>
);
};
export default AISearchComponent;Query Parameters (params.query):
limit: Maximum number of results (default: 1000)offset: Number of results to skip (default: 0)siteId: Filter by specific sitecontentType: Filter by content typelanguageId: Filter by languageAI Configuration (params.config):
threshold: Minimum similarity score (default: 0.5)distanceFunction: Vector distance algorithm (default: cosine)responseLength: Maximum response text length (default: 1024)useEditableDotCMSPage hook to enable UVE.dotcmsUrl matches your instance URL exactlyComponents Not Rendering: Empty spaces where content should appear
components propdevelopment mode for detailed loggingAsset Loading Issues: Images or files not loading
vite.config.ts/dA path is properly configuredBuild Errors: npm install fails
npm cache clean --forcenode_modules and reinstallRuntime Errors: Console errors about missing imports or components not rendering
@dotcms/reactServer Component Errors: Errors about React class components in Server Components
Possible Causes:
Solutions:
Split your code into Server and Client Components:
// app/page.tsx (Server Component)
import { DotCMSPage } from '@/components/DotCMSPage';
import { dotCMSClient } from '@/lib/dotCMSClient';
export default async function Page() {
const pageResponse = await dotCMSClient.page.get('/index');
return <DotCMSPage pageResponse={pageResponse} />;
}// components/DotCMSPage.tsx (Client Component)
'use client';
import { useEditableDotCMSPage, DotCMSLayoutBody } from '@dotcms/react';
import type { DotCMSPageResponse } from '@dotcms/types';
const COMPONENTS_MAP = {
Blog: BlogComponent,
Product: ProductComponent
};
export function DotCMSPage({ pageResponse }: { pageResponse: DotCMSPageResponse }) {
const { pageAsset } = useEditableDotCMSPage(pageResponse);
return <DotCMSLayoutBody page={pageAsset} components={COMPONENTS_MAP} />;
}Always fetch data in Server Components for better performance
Use Client Components only for rendering dotCMS interactive components
Enable Development Mode
<DotCMSLayoutBody
page={pageAsset}
components={COMPONENTS_MAP}
mode="development"
/>This will:
Check Browser Console
Network Monitoring
If you're still experiencing problems after trying these solutions:
We offer multiple channels to get help with the dotCMS React SDK:
dotcms-react when posting questions.When reporting issues, please include:
GitHub pull requests are the preferred method to contribute code to dotCMS. We welcome contributions to the dotCMS React 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 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.
latestFound an issue with this documentation? View the source