Skip to content
CanJS app and components built for the ArcGIS JS API 4.x
JavaScript HTML CSS
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.


Join the chat at

A configureable mapping app bundled with StealJS. Designed for configurability, and modularity. A work in progress. Inspired by cmv-app


Why can-arcgis?

I've worked with cmv, web app builder, and several other web map templates. Each has their advantages and cons. While can-arcgis requires some knowledge of javascript, it focuses on development, flexibility, and performance.

  • Easy: First and formost, adding an app to can-arcgis with layers, widgets, and map views should be easy and require only a minimal understanding of how the can-arcgis and the ArcGIS Javascript API works (what is my latitude and longitude, and what are my layer urls?) While Javascript knowledge is recommended, it is not required for creating a simple interactive application. Reading the documentation and following the steps should easily result in a successful application to deploy.
  • Development: can-arcgis uses some of the newest and best web technology. ES6, (or alternatively regular old ES5) can be used to write widgets and configs.
  • Flexibility: Custom dependencies like css and javascript can be easily installed and imported using only NPM or the package manager of your choice. Steal automatically loads modules from your package.json. Because of this, its very easy to utilize existing code and integrate third party frameworks.
  • Performance: Applications are built into separate modular config bundles with almost zero config. In addition, packages that are shared between configs are not built twice, instead they are automatically built into a shared file that will automatically be generated by steal-tools and loaded by a production version of steal.


  • Configure multiple apps using a simple JSON like syntax
  • Utilize existing Esri API widgets, layers, and map views
  • Easily compact, progressivly loaded bundles with steal-tools
  • Add functionality by creating widgets built using CanJS Components and stache (mustache like) templating language
  • Extend the application using builtin and custom hooks in ES6 or commonjs format


Quick Start


Install NodeJS and Git, then in a command prompt type:

git clone
cd repo
npm install 


Configs are loaded dynamically based on the current hash/route. By default, configs are stored in the config folder. Each config should be placed in its own folder, and related files can be placed next to that file. Each config gets its own url:!scene. For example, you can link to other configs within the app just by specifying its hash:

<a href="#!viewer">Viewer App</a>

This will navigate the user to the config file in config/viewer/viewer.js.

Customize a config file in the config folder. The syntax is a jsonlike structure that gets passed to esri's api constructors.

Each config module exports a default object.

export default {


Within this object are the configuration parameters for widgets, layers, and map views.


Options passed to the map constructor. These options are documented in the esri/Map api.

mapOptions: {
    layers: [] // layer config

A simplified json structure for creating esri layer objects. This should be inside the mapOptions object.

    layers: [{
            // path to feature layer class
            path: 'esri/layers/FeatureLayer',

            // options to pass to constructor
            options: {
                url: '/arcgis/rest/services/servicename/MapServer/132',
                id: 'workorders',
                outFields: ['*'],
                popupTemplate: {
                    // popup template


Options to be passed to the map view constructor.These options are documented in the esri/views/View api

viewOptions: {
    center: [-93.28697204589844, 44.294471740722656],
    zoom: 12


Array of widget config objects to be passed to the esri-map. These widgets are basic json structures that represent the types and parameters for widgets that the esri-map component should create.

These configs tell the esri-map how to find, load, and create widgets.

Additional widget types may be created by modifying teh esri-map/widgets files.

widgets: [{

    // type of widget
    // 'esri', 'renderer', or none
    type: 'esri', 
    // parent type 
    // options include 'expand', 'view', or 'invisible'
    parent: 'view', 

    // path to widget class
    path: 'dijit/layout/ContentPane', 

    // position to place widget in (if type is view)
    position: 'top-right',

        // customize widget or events after the widget 
        // gets created here

    // widget constructor options
    options: {
        content: 'Hello!'

Each config can include these properties or import them from other modules. For instance, you could have one file widgets.js that looks like this:

export default [{
    // widget config

and you can import that into your config by adding an import line to the top

import myWidgets from './widgets';

export default {
    // other config props
    widgets: myWidgets

For options that require esri modules, you'll need to use the esriPromise library along with the optionsPromise property. This allows you to resolve your options with a promise, rather than passing them in.

Example: configuring a BasemapToggle widget with optionsPromise:

// viewer.js config file
import {loadModules} from 'esri-loader';

export default {
    // ...
    // other config properties
    // ...
    widgets: [{
    path: 'esri/widgets/BasemapToggle',
    parent: 'view',
    type: 'esri',
    position: 'top-left',
    iconClass: 'esri-icon-basemap',
        return loadModules([
        ]).then(([Basemap, TileLayer, MapImageLayer]) => {
            const base = new Basemap({
                thumbnailUrl: '',
                id: 'aerial',
                baseLayers: [new TileLayer({
                    url: `${url}/basemaps/imagery_2016/MapServer`
                referenceLayers: [new MapImageLayer({
                    url: `${url}/basemaps/gray_labels/MapServer`
            return {
                nextBasemap: base


Open index-dev.html in a web server to get a live preview of the app. If you need a development web server,

npm i -g live-server

will start up a development server.


Before building, make sure you have added all of your config files as bundles to the package.json file. Specify your bundles in the steal.bundles array like "can-arcgis/config/viewer/viewer".

Each bundle will be scanned for dependencies using steal-tools and steal will automatically optimize the files into progressivly loaded bundles.

To build the application use the npm script build.

npm run build

Now you can copy the output dist folder and index.html to production.


Hooks are available to customize the core of this app. Hooks may be added or removed via the app/hooks.js config file.

Hooks are simple modules that export an object with one or more of the following methods. All methods accept a parameter based on the hook, and should return a promise that resolves to the same parameter. This ensures each hook is run sequentially, even if they are async.

Application Lifecycle

The application lifecycle begins when the app is constructed using new App(). Each method is called in order and the cycle repeats every time the app.configName changes. This is to ensure the config is reloaded and the map and widgets are created correctly.


Called immediately with the AppViewModel.


Called with the config that was loaded, but not yet set on the app. Useful for modifying the config before the map begins loading.


Called after config is set with the AppViewModel.


The final stage to the application loading process. A perfect time to initialize widgets on the view, change map layers, or any other runtime stuff on the view. Called with the AppViewModel


Why stealjs?

Steal provides a minimal effort configuration for your app to get started developing and generate distributable bundles.

Are dojo modules bundled?

At this time, dojo modules are loaded from the Esri CDN.

You can’t perform that action at this time.