Important Note About This Approach
While dotCMS strongly recommends implementing a fully headless architecture for new projects (we provide examples for it), we understand that some organizations are in a transition phase from traditional templating to a modern headless approach. This guide presents a hybrid solution that can be valuable in specific scenarios:
Transitioning Projects: If you have an existing dotCMS implementation using Velocity templates and want to gradually introduce React components
Legacy System Integration: When you need to maintain compatibility with existing Velocity-based templates while modernizing specific components
Incremental Migration: For organizations taking a step-by-step approach to moving from traditional to headless architecture
For new projects or complete redesigns, we strongly encourage adopting a fully headless approach to leverage the full benefits of modern web development practices and dotCMS's robust Page API and JavaScript SDK which can be fully integrated with our Universal Visual Editor.
Overview
This hybrid approach allows you to:
Develop React components locally with modern tools
Automatically build and upload components to dotCMS
Integrate components into pages using dotCMS's Velocity templating
Leverage npm packages and modern JavaScript features
Benefit from Vite's build optimization features
Workflow
You develop in the react-app folder
Vite builds and outputs files to a dotCLI sync folder, ex.: dotcli/files/live/[site]/application/react/dist/
dotCLI watches this folder and automatically pushes changes to dotCMS
Prerequisites
Before starting, ensure you have:
Node.js (v18 or higher) and npm installed
Access to a dotCMS instance
Good knowledge of React and dotCMS
dotCLI installed globally: npm install -g @dotcms/cli
A read-only API token from dotCMS (for security best practices)
Project Structure
The integration uses a two-folder structure:
project/
├── react-app/ # Your React application
│ ├── src/ # React source code
│ └── vite.config.js # Build configuration
│
└── dotcli/ # dotCLI synchronization directory
└── files/
└── application/
└── react/ # React integration files
├── dist/ # Built files
│ └── .vite/manifest.json # Asset mapping
└── react-widget.vtl # Velocity template
Step-by-Step Guide
Step 1: Project Setup
Create your project root directory:
mkdir my-dotcms-react-project
cd my-dotcms-react-project
Create a new React project with Vite:
npm create vite@latest react-app -- --template react
cd react-app
npm install
Create the dotCLI directory:
cd ..
mkdir dotcli
cd dotcli
dotcli login
Step 2: Configure Environment
In the react-app directory, create a .env file:
# dotCMS instance URL
VITE_DOTCMS_URL=http://localhost:8080
# Read-only API token
VITE_DOTCMS_AUTH_TOKEN=your_token_here
Configure Vite (vite.config.js):
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
// Base path for assets in production
base: process.env.NODE_ENV === 'production'
? '/application/react/dist'
: '/',
build: {
// Clean output directory before build
emptyOutDir: true,
// Generate manifest for asset tracking
manifest: true,
rollupOptions: {
input: {
// Entry point of your application
main: 'src/main.jsx'
},
output: {
// Output directory in dotCLI folder
dir: '../dotcli/files/live/en-us/demo.dotcms.com/application/react/dist'
}
}
}
})
Key configuration points
base: Sets the base URL for assets in production
emptyOutDir: Cleans the output directory before each build
manifest: Generates a manifest.json file for asset tracking
rollupOptions
input.main: Specifies the file of the entry point of your component
output.dir: Specifies where built files should be placed
Rollup
While Vite is a development tool offering a speedier, leaner experience for web projects. Rollup, is the module bundler taking small bits of code and compiling them into more complex code, optimizing it for browser compatibility.
In the Vite configuration you can pass rollupOptions, which allows you to configure even further your components builds, for example, you can have multiple components files, for more information check the rollup options page.
Step 3: Create Integration Template
Create react-widget.vtl in dotcli/files/application/react/:
## Fetch the Vite manifest file
#set($manifest = $json.fetch("$!{host.hostname}/application/react/dist/.vite/manifest.json"))
## Collect and deduplicate CSS files
#set($cssFiles = [])
#foreach($entry in $manifest.entrySet())
#if($entry.value.isEntry)
#if($entry.value.css)
#foreach($css in $entry.value.css)
#if(!$cssFiles.contains($css))
#set($void = $cssFiles.add($css))
<link rel="stylesheet" href="/application/react/dist/$css" />
#end
#end
#end
#end
#end
## Add entry point scripts
#foreach($entry in $manifest.entrySet())
#if($entry.value.isEntry)
<script type="module" src="/application/react/dist/$entry.value.file"></script>
#end
#end
## Root element for React
<div id="root"></div>
Note: $!{host.hostname} ensures the correct domain is used in different environments.
Development Workflow
Start the development server:
cd react-app
npm run dev
In a separate terminal, watch for file changes:
cd dotcli
dotcli files push files/live/en-us/demo.dotcms.com/application/react/dist -ra -rf --watch
Note: You can watch all the dotCLI files for push but I really recommend you only watch the dist folder where Vite will output the react build files.
Options explained:
--watch: Automatically push changes
--ra: Remove old assets
--rf: Remove old files
We need to remove old assets and files on every build because Vite by default add hashes to the file names to handle cached files in the browser and we don’t end up dotCMS file system with multiple unused JavaScript and CSS files.
dotCLI File Management
Common commands for managing files:
# Check status
dotcli status
# Pull all files
dotcli pull
# Push specific directory
dotcli files push <path>
# List changes
dotcli files list
Best Practices
Security
Always use read-only tokens for client-side applications
Keep environment variables secure
Never commit tokens to version control
Performance
Enable code splitting for larger applications
Utilize dynamic imports for conditional loading
Implement proper caching strategies
Development
Use TypeScript for better type safety
Implement proper error boundaries
Keep components focused and reusable
Multiple Components on One Page
When using multiple React components on the same page:
Use unique root IDs for each component
Create separate entry points for each component
Update the Velocity template to handle multiple components
Example:
<div id="component1-root"></div>
<div id="component2-root"></div>
Advanced Options
Code Splitting
// Dynamic import example
const MyComponent = React.lazy(() => import('./MyComponent'));
TypeScript Integration
npm install -D typescript @types/react @types/react-dom
CSS Modules
// Enable in vite.config.js
css: {
modules: true
}
Troubleshooting
Build Issues
Missing Assets
Verify Vite config base path
Check dotCLI connection status
Verify file permissions
Build Errors
Clear dist directory
Verify dependencies
Check for syntax errors
Runtime Issues
Component Not Rendering
Check browser console
Verify manifest.json loading
Inspect network requests
dotCLI Issues
Push Failures
Verify authentication
Check file permissions
Ensure correct path structure
Additional Resources
Conclusion
This integration approach offers a modern development experience while maintaining dotCMS's templating capabilities. You get the best of both worlds: React's component-based development and dotCMS's content management features.