Precursor
ServiceNow originally put out their own react renderer for their early workspace components. This allowed them to develop complex components in React while they were still working on their own component design library.
Once their own component library was fully built out they removed the @servicenow/ui-renderer-react
library from existence. I believe removing the react renderer was for a good reason and would have impacted the adoption of their new component system, but now its been a few years and that component system could use a content boost.
There are tons of amazing utilities and components built using React that cannot be used in now components simply because a react renderer didn't exist. If one did exist, it would allow us all to tap into thousands of useful components.
This was the main reason I originally built the @quixomatic/ui-renderer-react
library. However, that solution required manual modifications to global ServiceNow CLI files, making it difficult to distribute and maintain across teams and projects.
The New Solution: I've now created @quixomatic/ui-renderer-react-simple
- a React 18 renderer that requires zero global file modifications and provides one-command setup for any ServiceNow project.
The Problem We Solved
The original challenge wasn't just creating a React renderer - it was dealing with ServiceNow's babel plugin that transforms JSX. The plugin would detect React components but transform them to Snabbdom VNodes instead of React elements, causing the infamous "Objects are not valid as a React child" error.
Previous Solution: Required manually editing global babel plugin files in ~/.snc/.extensions/
.
New Solution: Creates a local "fake" @servicenow/ui-renderer-react
package that tricks the babel plugin into using proper React JSX transformation, plus automatically patches the babel plugin - no global modifications needed!
Installation (The Easy Way!)
The new solution provides one-command setup with automatic babel plugin patching:
# Install the package and React 18
npm install @quixomatic/ui-renderer-react-simple react@18 react-dom@18
# Run the automatic setup script (includes babel patching!)
npx setup-servicenow-react
# Complete the setup
npm install
That's it! No manual file editing, no global modifications, no complex setup instructions. The setup script automatically handles everything including babel plugin patching.
What the Setup Does
The setup script automatically:
-
Creates
src/node_modules/@servicenow/ui-renderer-react/
directory - Copies the React 18 renderer to masquerade as the official ServiceNow renderer
-
Updates your
package.json
with the local dependency - Ensures React 18 dependencies are present
- 🆕 Automatically patches ServiceNow's babel plugin for React support
- 🆕 Creates a backup of the original babel plugin for easy restoration
This tricks ServiceNow's babel plugin into thinking you have the official React renderer, so it uses React JSX transformation instead of Snabbdom.
CLI Commands
The new version includes convenient CLI commands:
# Complete setup (fake package + babel patch)
npx setup-servicenow-react
# Just patch the babel plugin (if already set up)
npx patch-servicenow-babel
# Restore the original babel plugin
npx restore-servicenow-babel
Usage
With the new setup, your components use the exact same pattern as before, but now import from the "official" ServiceNow package:
Component Setup
import { createCustomElement } from "@servicenow/ui-core";
import react from "@servicenow/ui-renderer-react"; // Note: Now imports from @servicenow!
import view from "./view";
import styles from "./styles.scss";
createCustomElement("my-react-component", {
renderer: { type: react },
view,
properties: {
title: { default: "Hello React 18!" },
count: { default: 0 }
},
actionHandlers: {
INCREMENT: ({ state, updateProperties }) => {
const { properties } = state;
updateProperties({ count: properties.count + 1 });
},
DECREMENT: ({ state, updateProperties }) => {
const { properties } = state;
updateProperties({ count: properties.count - 1 });
}
},
styles
});
React Component with Modern Hooks
import React, { useState, useEffect, useCallback } from "react";
export default function MyReactComponent(state) {
const { dispatch, properties } = state;
const { title, count } = properties;
// All React 18 hooks work perfectly!
const [localState, setLocalState] = useState('');
const [isLoading, setIsLoading] = useState(false);
// useEffect works
useEffect(() => {
console.log('Component mounted with React 18!');
return () => console.log('Cleanup on unmount');
}, []);
// useCallback works
const handleIncrement = useCallback(() => {
setIsLoading(true);
dispatch('INCREMENT');
setTimeout(() => setIsLoading(false), 300);
}, [dispatch]);
return (
<div className="my-react-component">
<h1>{title}</h1>
<div className="counter">
<button
onClick={() => dispatch('DECREMENT')}
disabled={isLoading}
>
-
</button>
<span className="count">{count}</span>
<button
onClick={handleIncrement}
disabled={isLoading}
>
{isLoading ? '...' : '+'}
</button>
</div>
<input
type="text"
value={localState}
onChange={(e) => setLocalState(e.target.value)}
placeholder="Local React state works too!"
/>
</div>
);
}
Hybrid Components (Snabbdom + React)
You can still mix Snabbdom parents with React children:
import { createCustomElement } from "@servicenow/ui-core";
import snabbdom from "@servicenow/ui-renderer-snabbdom";
import './my-react-component'; // Import the React component
const view = (state, { updateState }) => {
const { showReact = false } = state;
return (
<div>
<h1>Snabbdom Parent Component</h1>
<button on-click={() => updateState({ showReact: !showReact })}>
{showReact ? 'Hide' : 'Show'} React Component
</button>
{showReact && (
<my-react-component title="Embedded React!" count={5} />
)}
</div>
);
};
createCustomElement("hybrid-component", {
renderer: { type: snabbdom },
view
});
New Features & Benefits
React 18 Support
- ✅ All React 18 hooks (
useId
,useDeferredValue
,useTransition
, etc.) - ✅ Concurrent rendering features
- ✅ Automatic batching
- ✅ Improved performance with
createRoot
API
Zero Global Modifications (Automated!)
- ✅ 🆕 Automatic babel plugin patching - no manual editing required
- ✅ 🆕 Backup/restore functionality for babel plugin
- ✅ 🆕 Cross-platform babel plugin detection (Windows/macOS/Linux)
- ✅ No need to manually edit ServiceNow CLI files
- ✅ Works with any ServiceNow CLI version
- ✅ Team-friendly - no setup coordination needed
- ✅ Works in CI/CD environments
Professional Distribution
- ✅ Published on npm with semantic versioning
- ✅ Automated testing across platforms
- ✅ Comprehensive documentation and examples
- ✅ Open source with contribution guidelines
Better Error Handling
- ✅ Built-in React error boundaries
- ✅ Proper error logging and reporting
- ✅ Graceful degradation on component failures
- ✅ 🆕 Graceful babel patching failure handling
Migration from Original Solution
If you're using the original @quixomatic/ui-renderer-react
:
# Remove the old package
npm uninstall @quixomatic/ui-renderer-react
# Install the new solution
npm install @quixomatic/ui-renderer-react-simple react@18 react-dom@18
# Run the automated setup (includes babel patching!)
npx setup-servicenow-react
npm install
# Update your imports (only change needed!)
// Old
import react from "@quixomatic/ui-renderer-react";
// New
import react from "@servicenow/ui-renderer-react";
🆕 Key Migration Benefits:
- No more manual babel file editing - everything is automated
-
Easy restoration - restore original babel plugin anytime with
npx restore-servicenow-babel
- Better reliability - automatic detection and patching across different system configurations
Your components will work exactly the same - just with React 18 features and zero manual configuration!
Babel Plugin Details
How It Works
The babel plugin patcher:
-
Automatically locates ServiceNow's babel plugin file in
~/.snc/.extensions/ui-component/
- Creates a backup before making any changes
-
Patches the React renderer configuration to use the correct module path instead of the faulty
u()
function - Removes old renderer entries if they exist
- Verifies the patch was applied successfully
Manual Control
If you need manual control over the babel plugin:
# Patch just the babel plugin
npx patch-servicenow-babel
# Restore the original babel plugin
npx restore-servicenow-babel
# Verify current patch status
npx patch-servicenow-babel verify
Troubleshooting Babel Issues
If babel patching fails:
# Check ServiceNow CLI installation
snc --version
# Try patching separately
npx patch-servicenow-babel
# If needed, restore and retry
npx restore-servicenow-babel
npx patch-servicenow-babel
Example Repository
Check out the complete example repository which includes:
- Basic Examples: Simple counter and form components
- Advanced Examples: Complex forms with validation
- Hook Examples: Demonstrating all React 18 hooks
- Hybrid Examples: Mixing Snabbdom and React
- Documentation: Complete setup and usage guides
Check out a full scaffolded project repository which includes nested react components using shadcn and tailwind within a parent snabbdom component.
Troubleshooting
The new solution eliminates most common issues, but if you encounter problems:
"Objects are not valid as a React child"
This usually means the setup didn't complete:
# Re-run the complete setup
npx setup-servicenow-react
npm install
React Hooks Not Working
Ensure React 18 is installed:
npm list react react-dom
# Should show 18.x.x versions
Babel Plugin Issues
If babel patching fails:
# Try patching separately
npx patch-servicenow-babel
# If that fails, check ServiceNow CLI
snc --version
Import Errors
Make sure you're importing from the correct package:
import react from "@servicenow/ui-renderer-react"; // Correct
Conclusion
With this new solution, integrating React 18 into ServiceNow components is now truly effortless. The one-command setup with automatic babel plugin patching eliminates all complexity and manual configuration that made previous solutions difficult to distribute.
Key Improvements in v1.1.0:
- 🚀 Automatic babel plugin patching - no manual editing required
- 🔄 Backup/restore functionality for easy rollback
- 🛡️ Cross-platform compatibility - works on Windows, macOS, and Linux
- ⚡ One-command setup - literally just
npx setup-servicenow-react
This opens the door to using the entire React ecosystem within ServiceNow:
- UI Libraries: Material-UI, Ant Design, Chakra UI, shadcn/ui
- Utilities: React Hook Form, React Query, Zustand
- Visualization: D3 React components, Chart libraries
- And thousands more from the React community
The solution is production-ready, team-friendly, future-proof, and now completely automated. Go forth and be reactive - with React 18! 🚀
Package Information:
- NPM: @quixomatic/ui-renderer-react-simple
- GitHub: Repository
- License: MIT
- Latest Version: 1.1.0 (with automatic babel plugin patching)
Top comments (5)
I've managed to get this working when using the snc cli locally. However when I attempt to deploy to the instance I always get 403 forbidden errors.
If I change the babel-plugin-jsx-now-pragmatic.js file back to the default ServiceNow one I can then upload to the instance but the react components no longer build or deploy.
Anyone had this issue?
I love the concept but I'm having some trouble implementing. Is there anyway you can publish a repo with demo code of the snc ui-component?
Just posted the example repository in the article. You can find it here.
Yeah, I can put together a repo and post it on the article.
I am getting below error after I followed everything mentioned in the article.
