Skip to content
Jimu modular architecture showing core packages and their relationships
Jimu modular architecture showing core packages and their relationships.

What is Jimu?

Jimu is the core JavaScript framework that powers ArcGIS Experience Builder. It serves as the foundation that makes Experience Builder extensible and customizable. It provides:

  • Extensibility framework: The architecture that allows you to create custom components
  • Configuration system: A way to customize experiences without coding
  • Package library: A collection of libraries for different development needs
  • Development tools: Utilities and patterns for building applications

Jimu is designed with modularity in mind, allowing you to use only what you need while maintaining consistency across applications.

You use Jimu to create custom:

  • Widgets that extend the functionality of Experience Builder
  • Extensions that enhance the capabilities of Experience Builder
  • Themes that can be applied to your experiences
  • Configuration that allows you to customize the behavior and appearance of your experiences

Jimu architecture

Jimu follows a modular architecture pattern with these key principles:

Modular design

Jimu follows a modular architecture where each package has a focused responsibility. This design allows developers to use only the packages they need while maintaining consistency across applications.

PackageDescription
jimu-coreUtilities, state management, data sources, and base classes
jimu-uiUser interface components and theming
jimu-arcgisArcGIS functionality and map integration
jimu-for-builderBuilder components and utilities
jimu-themeTheme management and styles
jimu-layoutsLayout management and components
jimu-for-testTesting utilities
jimu-iconsIcon management and SVGs

Extensibility pattern

Jimu uses a consistent pattern for extending functionality. Below is an example of creating a custom widget:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
// Example: Creating a custom widget
import { type AllWidgetProps } from 'jimu-core'

const Widget = (props: AllWidgetProps<any>) => {
  return (
    <div className="my-widget jimu-widget ">
      <p>My Widget</p>
    </div>
  )
}

export default Widget

Message action and data action are JavaScript classes, and the recommended way to create them is to extend the AbstractDataAction and AbstractMessageAction classes.

Use dark colors for code blocksCopy
1
2
3
export default class MyDataAction extends AbstractDataAction {}

export default class MyMessageAction extends AbstractMessageAction {}

Extensions are JavaScript classes, but different extensions have different interfaces. The recommended way is to implement the interface.

Use dark colors for code blocksCopy
1
export default class MyExtension implements extensionSpec.SomeExtensionInterface {}

Configuration-driven

Jimu applications are configuration-driven allowing runtime customization. Below is an example of a configuration file for a widget.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
{
  "widgets": {
    "my-widget": {
      "label": "My Widget",
      "uri": "widgets/my-widget",
      "config": {
        "title": "Custom Title"
      }
    }
  }
}

Core packages

Jimu is organized into packages where each package serves development needs. Below is an overview of each package:

jimu-core

The jimu-core package provides utilities and base classes for building experiences.

ComponentDescription
Base classes and interfacesSuch as Message, MessageAction, DataAction, BaseExtension, DataSource
ManagersSuch as WidgetManager, MessageManager, DataActionManager, ExtensionManager, DataSourceManager, SessionManager
Redux actionsSuch as appActions to manage state
Utility functionsSuch as utils, urlUtils, portalUrlUtils, appConfigUtils, dataSourceUtils, geometryUtils
ComponentsSuch as DataSourceComponent, ExpressionResolverComponent
Module loadersmoduleLoader, loadArcGISJSAPIModules

jimu-ui

The jimu-ui package provides all UI components for building experiences. It uses emotion-js for styling and organizes components into index, basic, and advanced categories.

ComponentDescription
IndexCommonly used UI components such as Button, TextInput, Modal
BasicComponents that provide focused functionality such as DatePicker, ColorPicker
AdvancedComplex components that are specific to Experience Builder such as ExpressionBuilder, DataSourceSelector

The recommended way to create UI components is to use styled components. You can find examples of styled components in the jimu-ui package documentation.

Below is an example of using the UI components:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Button, TextInput, Modal } from 'jimu-ui';
import { css } from 'jimu-core';
import { React } from 'jimu-core';

// Example: Using UI components with theming
const widgetStyle = css`
  background: var(--background-color);
  color: var(--text-color);
  padding: 1rem;
`;

export default function MyWidget() {
  const [isModalOpen, setIsModalOpen] = React.useState(false);

  return (
    <div css={widgetStyle}>
      <TextInput placeholder="Enter data..." />
      <Button type="primary" onClick={() => setIsModalOpen(true)}>Submit</Button>
      <Modal isOpen={isModalOpen}>
        <div>Modal content</div>
      </Modal>
    </div>
  );
}

jimu-arcgis

The jimu-arcgis package contains components, interfaces, classes, properties and methods for working with the ArcGIS Maps SDK for JavaScript.

Below is a list of some of the classes and components in the jimu-arcgis package.

ComponentDescription
JimuMapViewClass for accessing MapView or SceneView instances
JimuMapViewComponentComponent for map widget and view integration
JimuLayerViewClass for working with layer views
JimuLayerViewComponentComponent for layer view operations

Below is an example of using the map components:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
import { JimuMapViewComponent } from 'jimu-arcgis';

// Example: Map integration
<JimuMapViewComponent
  useMapWidgetId={useMapWidgetId}
  onActiveViewChange={(jmv) => {
    // Handle map view changes
    jmv.view.when(() => {
      console.log('Map view ready');
    });
  }}
/>

jimu-for-builder

The jimu-for-builder package is designed to support developing the widget setting page. The setting page is the section of the builder where the experience author adds widget configuration.

The main components in this package are: builderAppSync, appBuilderSync, and WidgetSettingManager. The package extends the Redux store to add builder state, such as appStateInBuilder and builder.

jimu-theme

The jimu-theme package provides theme management and styles for building experiences. This package provides:

  • styled method for creating styled components
  • withTheme method for wrapping a component with a theme prop via a higher order component
  • useTheme component function which is a hook for accessing theme
  • Theme-related interfaces such as ThemeVariable and ThemeOptions
ComponentDescription
styledThe method for creating styled components
useThemeThe react hook for accessing theme variables
withThemeThe method for wrapping a component with a theme prop via a higher order component
ThemeVariableThe interface of theme variables.
RawThemeOptionsThe interface of theme options in json( variables.json in theme folder).

jimu-layouts

The jimu-layouts package provides layout management and components. It includes two entries:

  • layout-runtime (lightweight entry) for when the app is opened at runtime. It exports the selectors which provides the layout tree search functionality. It also exports the LayoutEntry component for widget layout capabilities.
  • layout-builder (entry) for when the app is opened in the builder.
  • searchUtils for layout tree operations

jimu-data-sources

The jimu-data-sources package provides the implementation of data sources. For most use cases, you should use the DataSourceComponent exported from jimu-core rather than accessing this package directly.

jimu-for-test

The jimu-for-test package provides testing utilities. Utility functions include wrapWidget, withThemeRender, withIntlRender, withStoreRender, mockService, mockItem, initGlobal, getInitState, and widgetRender.

Below is an example of using the testing utilities to test a checkbox component.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import * as React from 'react'
import { render } from '@testing-library/react'
import { Checkbox } from 'jimu-ui'

describe('checkbox component test', () => {
  it('test mount', () => {
    const { container } = render(<Checkbox />)
    expect(container.firstChild).not.toBeNull()
  })

  it('test default jimu class name', () => {
    const { container } = render(<Checkbox />)
    expect(container.querySelector('.jimu-checkbox')).toBeTruthy()
  })

  it('test "checked" attribute', () => {
    const { container } = render(<Checkbox checked />)
    expect(container.querySelector('.jimu-checked')).toBeTruthy()
  })

  it('test "indeterminate" attribute', () => {
    const { container } = render(<Checkbox indeterminate />)
    expect(container.querySelector('.indeterminate')).toBeTruthy()
  })

  it('test "disabled" attribute', () => {
    const { container } = render(<Checkbox disabled />)
    expect(container.querySelector('.jimu-disabled')).toBeTruthy()
  })
})

Another example of using the testing utilities to test a button component. You can find this example in <EXB Developer Edition install path>/client/dist/widgets/common/button/tests/button.test.tsx.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import { React, Immutable, getAppStore, appActions } from 'jimu-core'
import ButtonWidget from '../src/runtime/widget'
import { wrapWidget, widgetRender, getInitState, getDefaultAppConfig } from 'jimu-for-test'
import '@testing-library/jest-dom'
getAppStore().dispatch(appActions.updateStoreState(getInitState().merge({ appConfig: getDefaultAppConfig() })))

const render = widgetRender(false)
const Widget = wrapWidget(ButtonWidget)
describe('button widget test', function () {
  describe('default config', function () {
    const config = Immutable({
      functionConfig: {
        text: 'Please configure link',
        toolTip: '',
        linkParam: {
        }
      },
      styleConfig: {
        themeStyle: {
          quickStyleType: 'default'
        }
      }
    })

    it('button widget should be render', () => {
      const { queryBySelector } = render(<Widget config={config}/>)
      expect(queryBySelector('.widget-button-link')).not.toBeNull()
    })
  })

  describe('test value config', function () {
    const config = Immutable({
      functionConfig: {
        text: 'textTest',
        toolTip: 'testToolTip',
        linkParam: {
          value: 'detail-page',
          linkType: 'PAGE'
        }
      },
      styleConfig: {
        themeStyle: {
          quickStyleType: 'default'
        }
      }
    })

    it('button widget should be render', () => {
      const renderResult = render(<Widget config={config}/>)

      expect(renderResult.queryBySelector('.widget-button-link')).toBeInTheDocument()
      expect(renderResult.queryByText('textTest')).toBeInTheDocument() //text ok
      expect(renderResult.queryByTitle('testToolTip')).toBeInTheDocument() // toolTip ok
      // renderResult.queryBySelector('.widget-button-link').getAttribute('href'); // link ok
    })
  })
})

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.