SDK: @dotcms/react Library
The @dotcms/react SDK is the DotCMS official React library. It empowers React developers to build powerful, editable websites and applications in no time.
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!
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.
Install Dependencies#
npm install @dotcms/react@latest
This 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 data
dotCMS Client Configuration#
import { 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 });
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 user experience, making it a strategic enhancement for your project.
1. Configure Vite#
// 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.
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 with React.
// /components/my-dotcms-image.tsx import type { DotCMSBasicContentlet } from '@dotcms/types'; export const MyDotCMSImageComponent = ({ inode, title }: DotCMSBasicContentlet) => { return <img src={`/dA/${inode}`} alt={title} />; }
Quickstart: Render a Page with dotCMS#
The following example demonstrates how to quickly set up a basic dotCMS page renderer in your React application. This example shows how to:
- Create a standalone component that renders a dotCMS page
- Set up dynamic component loading for different content types
- Handle both regular page viewing and editor mode
- Subscribe to real-time page updates when in the Universal Visual Editor
// /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;
Example Project 🚀#
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!
SDK Reference#
All components and hooks should be imported from @dotcms/react:
DotCMSLayoutBody#
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. |
Next.js App Router#
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} />; }
Usage#
import type { DotCMSPageAsset } 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} />; };
buildSlots#
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.
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 default components for unmapped content types and provides visual indicators and console logs for empty containers and missing mappings.
Component Mapping#
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 };
- Keys (e.g.,
Blog,Product): Match your content type variable names in dotCMS - Values: Dynamic imports of your React components that render each content type
- Supports lazy loading through dynamic imports
- Components must be standalone or declared in a module
[!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#
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): Support 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 |
Usage#
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;
Editor Integration#
- Detects UVE edit mode and enables inline TinyMCE editing
- Triggers a
Saveworkflow action on blur without needing full content dialog.
DotCMSBlockEditorRenderer#
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. |
Usage#
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} /> ); };
Next.js Server Components#
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'} /> ); }
Recommendations#
- Should not be used with
DotCMSEditableText - Take into account the CSS cascade can affect the look and feel of your blocks.
DotCMSBlockEditorRendereronly works with Block Editor fields. For other fields, useDotCMSEditableText.
📘 For advanced examples, customization options, and best practices, refer to the DotCMSBlockEditorRenderer README.
DotCMSShow#
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 |
Usage#
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#
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() |
Service Lifecycle & Operations#
When you use the hook, it:
- Initializes the UVE with your page data
- Sets up communication channels with the editor
- Tracks content changes in real-time
- Updates your page automatically when:
- Content is edited inline
- Blocks are added or removed
- Layout changes are made
- Components are moved
- Cleans up all listeners and connections on destroy
Usage#
'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 pageAsset={pageAsset} components={COMPONENTS_MAP} />; }
useDotCMSShowWhen#
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 |
Usage#
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#
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 |
Return Value#
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 |
Usage#
'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;
Features#
- Automatic State Management: Handles loading, success, error, and idle states
- Type-Safe Results: Generic typing ensures type safety for your content types
- Search Validation: Automatically validates and trims search prompts
- Configurable Parameters: Support for pagination, thresholds, and AI configuration
- Error Handling: Provides detailed error information through status state
- Reset Capability: Easy state reset for clearing search results
Configuration Options#
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 language
AI Configuration (params.config):
threshold: Minimum similarity score (default: 0.5)distanceFunction: Vector distance algorithm (default: cosine)responseLength: Maximum response text length (default: 1024)
Troubleshooting#
Common Issues & Solutions#
Universal Visual Editor (UVE)#
- UVE Not Loading: Page loads but UVE controls are not visible
- Possible Causes:
- Incorrect UVE configuration
- Missing API token permissions
- Missing the
DotCMSEditablePageServicecall to enable UVE.
- Solutions:
- Verify UVE app configuration in dotCMS admin
- Check API token has edit permissions
- Ensure
dotcmsUrlmatches your instance URL exactly
- Possible Causes:
Missing Content#
-
Components Not Rendering: Empty spaces where content should appear
- Possible Causes:
- Missing component mappings
- Incorrect content type variable names
- Solutions:
- Check component registration in
componentsprop - Verify content type variable names match exactly
- Enable
developmentmode for detailed logging
- Check component registration in
- Possible Causes:
-
Asset Loading Issues: Images or files not loading
- Possible Causes:
- Proxy configuration issues
- CORS restrictions
- Solutions:
- Verify proxy settings in
vite.config.ts - Check network tab for CORS errors
- Ensure
/dApath is properly configured
- Verify proxy settings in
- Possible Causes:
Development Setup#
-
Build Errors:
npm installfails- Solutions:
- Clear npm cache:
npm cache clean --force - Delete
node_modulesand reinstall - Verify Node.js version compatibility
- Clear npm cache:
- Solutions:
-
Runtime Errors: Console errors about missing imports or components not rendering
- Solutions:
- Check all imports are from
@dotcms/react - Verify all peer dependencies are installed
- Update to latest compatible versions
- Check all imports are from
- Solutions:
Next.js App Router Integration#
-
Server Component Errors: Errors about React class components in Server Components
-
Possible Causes:
- Using dotCMS components directly in Server Components
- Missing 'use client' directive
- Incorrect data fetching pattern
-
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 pageAsset={pageAsset} components={COMPONENTS_MAP} />; } -
Always fetch data in Server Components for better performance
-
Use Client Components only for rendering dotCMS interactive components
-
-
Debugging Tips#
-
Enable Development Mode
<dotcms-layout-body [page]="pageAsset()" [components]="components()" mode="development" />This will:
- Show detailed error messages
- Highlight unmapped components
- Log component lifecycle events
-
Check Browser Console
- Check for errors in the browser console
- Check for errors in the browser network tab
-
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:
- Search existing GitHub issues
- Ask questions on the community forum to engage with other users.
- 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 React 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-reactwhen 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
- React 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 React SDK! If you'd like to contribute, please follow these steps:
- 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.
mainFound an issue with this documentation? Report it on GitHub