Working with widgets is an essential part of the ArcGIS API for JavaScript. Typically, a widget is thought of as a piece of the API that encapsulates a specific set of functionality. The API provides ready-to-use widgets with predefined functionality. There may be circumstances where you may need to customize a widget to your own specifications. In these cases, a custom widget may be what is needed. The following steps go through creating a very basic "Hello World" widget. These steps are provided as a basic foundation needed when creating your own custom widget. For a more detailed example, please refer to the Recenter widget sample.

Before you get started

Widget development is written in TypeScript and compiled to JavaScript. In order to follow the steps below, you will need to make certain you have TypeScript installed. Any text editor should suffice as long as you are able to compile the resulting TypeScript code into a resulting JavaScript file. In addition to this, you should also be familiar with JSX. This allows you to define the widget's UI similarly to HTML. Lastly, widget development is largely reliant upon familiarity of Implementing Accessor. More information on these requirements can be found at:

1. Create project directory and file structure

There is no one-specific IDE required for writing TypeScript. As long as you have access to the compiler needed to generate the underlying JavaScript files, any IDE should work.

Create a new directory to contain all the widget files. In the screen capture below, Visual Studio Code is used. This is a free download and works well with TypeScript. A new folder called HelloWorld is created. This folder should also be accessible by a web server.

Inside the HelloWorld directory, create another directory called app, this will only contain the widget's files. Inside the app directory, create a new file called HelloWorld.tsx.

Each widget belongs in a .tsx file, which allows you to use JSX to define the UI.

In the HelloWorld directory, create a tsconfig.json file. This file is necessary to tell the compiler how the project should be compiled.

The first two lines refer to triple slash directives used in TypeScript. These import directives are still needed as our TypeScript library relies on some functionality within them. The __extends and __decorate names are helpers when extending a class or decorating members of a class.

The next three lines are importing specific modules used for the widget implementation.

Take note that even though tsx is not explicitly being used in the code sample, it must still be imported along with @renderable.

This is not required, but if using the tsconfig.json option "noUnusedLocals": true, you will need to reference tsx within the code, similar to

import { renderable, tsx } from"esri/widgets/support/widget";
tsx; // Reference tsx here, this will be used after compilation to JavaScript

Lastly, we set a CSS object with base and emphasis properties. These are used within the widget's render() method and is used as a lookup for classes, thereby centralizing all the CSS within the widget.

Extend Widget base class

Here, we are extending the Widget base class. The @subclass decorator is used in conjunction with declared and is necessary as they are both key components needed for constructing subclasses off of a given base class.

All of these properties have @property and @renderable decorators. The first one is used to define an Accessor property. By specifying this decorator within the property, you give this property the same behavior as other properties within the API. By specifying @renderable you are telling the widget to schedule a renderer whenever the property is modified.

The render() method is the only required member of the API that must be implemented. This method must return a valid UI representation. JSX is used to define the UI. With this said, it is important to note that we are not using React. The transpiled JSX is processed using a custom JSX factory, therefore there is no direct equivalency between implementing a custom widget and a React component.

The snippet above sets two variables called greeting and classes. By default, functions referenced in your elements will have this set to the actual element. You can use the special bind attribute to change this, e.g. bind={this}.

The class property cannot be changed within the render() method. If there are dynamic classes, use the classes helper method instead.

Add the custom widget reference

Once you've created the custom widget, you need to load it. This comes down to telling Dojo's module loader how to resolve the path for your widget which means mapping a module identifier to a file on your web server. On the SitePen blog, there's a post discussing the differences between aliases, paths and packages which may help alleviate any questions specific to this.

Add a script element that handles loading this custom widget as seen below.

Additional information

The files used in this example can be accessed from the source code above. Please use these files as a starting point to begin creating your own custom classes and widgets. Once you have this complete and would like to see a more advanced custom widget, please continue to the Custom Recenter widget sample.