Next Experience: UI Renderer React
Tags: nextexperience, npm, tutorial, servicenow
Published: 2023-01-13 16:35:07

## 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](https://www.npmjs.com/package/@quixomatic/ui-renderer-react).

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](https://github.com/Quixomatic/example-react-component). 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.

```shell
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.

```shell
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.

```javascript
import react from "@quixomatic/ui-renderer-react";
```

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

```javascript
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](https://gist.github.com/Quixomatic/990d64edf450485ed7a04c50fd366ddd#file-babel-plugin-jsx-now-pragmatic-js).

## Usage

Now that we know how to setup our `createCustomElement` call lets take a look at an example component.

`exampleReactComponent.js`

```jsx
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`

```jsx
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`

```jsx
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](https://bestofjs.org/) is a great place to lookup and find projects and components that may be useful to you. Go forth and be reactive!