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 built the @quixomatic/ui-renderer-react
library and posted it on npmjs.io.
With a functioning react renderer we can use in place of the snabbdom renderer, we can get started.
Example GIT Repository
Here is an example GIT repo to get you started. Please take note of the instructions in the README
file if you use this repository.
Preparation
First things first you need to install the package in your component project.
npm install @quixomatic/ui-renderer-react --save
This package also expects you to have react@18.2.0
and react-dom@18.2.0
installed in your project.
npm install react@18.2.0 react-dom@18.2.0 --save
The renderer can be imported at any of the components or subcomponents within your project.
import react from "@quixomatic/ui-renderer-react";
This renderer is utilized inside the standard createCustomElement
that is imported from @servicenow/ui-core
.
createCustomElement("test-react-component", {
renderer: { type: react },
...
Normally the renderer would be passed {type: snabbdom}
to serve as the renderer, but in our case we want to use react within our component.
Important Step!
Finally, you have to update an existing file in your project's node_modules
folder. The file in question tells babel how to treat components that use @quixomatic/ui-renderer-react
as its renderer. Without updating this file the renderer will not work. The path to the file is: MAIN_PROJECT_FOLDER/node_modules/@servicenow/ui-build-component-utils/babel-plugin-jsx-now-pragmatic.js
. You can replace the entire contents of that file with this gist file.
Usage
Now that we know how to setup our createCustomElement
call lets take a look at an example component.
exampleReactComponent.js
import { createCustomElement } from "@servicenow/ui-core";
import react from "@quixomatic/ui-renderer-react";
import styles from "./styles.scss";
import view from "./view";
createCustomElement("inner-react-component", {
renderer: { type: react },
view,
properties: {
test: {
default: null
},
count: {
default: 0
}
},
actionHandlers: {
["handlePlus"]: ({ action, state, updateState, updateProperties }) => {
const { properties } = state;
const { count } = properties;
updateProperties({
count: count + 1
});
},
["handleMinus"]: ({ action, state, updateState, updateProperties }) => {
const { properties } = state;
const { count } = properties;
updateProperties({
count: count - 1
});
}
},
styles
});
Even though we are using react we still get access to calling dispatch
so that we can tap into our actionHandlers
. We could instead use react hooks like useState
to manage state and properties, but since our project may contain parent components that are using snabbdom using dispatch
just makes sense. Both snabbdom and react components can support dispatch
and actionHandlers
.
Now that we have createCustomElement
set up, let's take a look at our view.js
file which will contain the actual react component itself.
view.js
import React, { useState } from "react";
export default function InnerReactComponent(state) {
const { dispatch, helpers, properties } = state;
const { test, count } = properties;
function plusHandler() {
dispatch("handlePlus");
}
function minusHandler() {
dispatch("handleMinus");
}
return (
<div className="test-react-component">
<p>{test}</p>
<div className="wrapper">
<h1>{count}</h1>
<button onClick={plusHandler}>+</button>
<button onClick={minusHandler}>-</button>
</div>
</div>
);
}
With the view created we can see that whenever the plusHander
or minusHandler
functions are called they will dispatch
events to our actionHandlers
which can manage the state of the count
property. This is an extremely similar process to how regular snabbdom components manage their state and properties.
Now we just want to render our react component and the best way to do that is to place it within a parent snabbdom component.
parentComponent.js
import { createCustomElement } from "@servicenow/ui-core";
import snabbdom from "@servicenow/ui-renderer-snabbdom";
import styles from "./styles.scss";
import './test-react-component';
const view = (state, { updateState }) => {
const { componentId } = state;
const testMessage = 'hello there';
return <div id={componentId}>
<span>Stuff in the parent snabbdom component</span>
<inner-react-component test={testMessage } />
</div>;
};
createCustomElement("parent-snabbdom-component", {
renderer: {
type: snabbdom
},
view,
styles
});
Conclusion
With this basic setup you can now render a react component in your now/next components for ServiceNow. This should open the door to many new possibilities by utilizing existing components made and shared with React. BestOfJS.org is a great place to lookup and find projects and components that may be useful to you. Go forth and be reactive!
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.