DEV Community

James Freund
James Freund

Posted on • Updated on

Next Experience: UI Renderer React

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The renderer can be imported at any of the components or subcomponents within your project.

import react from "@quixomatic/ui-renderer-react";
Enter fullscreen mode Exit fullscreen mode

This renderer is utilized inside the standard createCustomElement that is imported from @servicenow/ui-core.

createCustomElement("test-react-component", {
    renderer: { type: react },
...
Enter fullscreen mode Exit fullscreen mode

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
});
Enter fullscreen mode Exit fullscreen mode

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>
    );
}
Enter fullscreen mode Exit fullscreen mode

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
});

Enter fullscreen mode Exit fullscreen mode

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 (4)

Collapse
 
vincepg13 profile image
Vincent Fisher

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?

Collapse
 
fauverism profile image
Robert Fauver

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?

Collapse
 
quixomatic profile image
James Freund

Just posted the example repository in the article. You can find it here.

Collapse
 
quixomatic profile image
James Freund

Yeah, I can put together a repo and post it on the article.