logo
dojo/framework

Widget fundamentals

Widgets are the fundamental building blocks from which all Dojo applications get constructed. Widgets are the primary units of encapsulation and represent everything from individual elements on a user interface to higher-level containers such as forms, sections, pages, or even complete applications.

Preamble: minimizing complexity

A single widget typically represents a single responsibility within an application. Trivial responsibilities naturally translate into individual widgets, whereas complex responsibilities need to get separated into several interdependent areas. Each sub-area can then be implemented as its own widget, with one or more parent container widget(s) coordinating interactions between all the decomposed widgets. In this sort of hierarchy, the root widget can be seen to implement the larger responsibility as a whole, but in reality it does so via a composition of many other simpler widgets.

The set of all requirements for a complete application can be considered such a single, complex responsibility. Implementing the complete set within Dojo results in a hierarchy of widgets, typically starting from a root 'Application' widget which then branches out through several layers of functionality, eventually reaching leaf nodes that represent individual elements within an HTML page.

Benefits of simplicity

Keeping widgets as simple as possible is desirable for several reasons. For single widgets, reduced complexity means greater isolation of responsibility (reduced scope); simpler comprehensive testing; reduced chance of bugs; more targeted bug fixing; as well as a wider potential for component re-use.

For complete applications, simple widgets also allows for easier understanding of all constituent components, as well as how they get combined.

Together these benefits lead to simpler ongoing maintenance and an ultimate reduction in the overall cost of building and running an application.

Basic widget structure

At their heart, widgets are simply render functions which return VDOM nodes that form the widget's structural representation within a web page. However, applications typically require more logic than a simple list of HTML elements, so meaningful widgets are usually comprised of more than just a simple render function.

Widgets are typically housed within their own self-named TypeScript modules, with the widget definition as the default export from each module.

The simplest way of representing widgets is based on plain functions, starting from a render function factory definition. Dojo provides a create() primitive in the @dojo/framework/core/vdom module that allows authors to define their widget render function factories. Named render functions are preferred as they can help with debugging, but this is not a requirement; widgets can also by identified via an exported variable holding their factory definition.

Dojo optionally supports class-based widgets for applications that prefer the structure of classes over functions. Such widgets inherit from WidgetBase, provided by the @dojo/framework/core/WidgetBase module, and are required to implement a render() method.

The following example shows a trivial yet complete widget within a Dojo application:

src/widgets/MyWidget.ts

Function-based Dojo widget variant:

import { create } from '@dojo/framework/core/vdom';

const factory = create();

export default factory(function MyWidget() {
    return [];
});

Class-based Dojo widget variant:

import WidgetBase from '@dojo/framework/core/WidgetBase';

export default class MyWidget extends WidgetBase {
    protected render() {
        return [];
    }
}

Because this widget returns an empty array from its render function, it has no structural representation within an application's output. Widgets typically return one or more virtual DOM nodes in order to provide meaningful structure within the application's HTML output.

The process of translating virtual DOM nodes to output on a web page is handled by Dojo's rendering system.

Widget styling

Styling of a widget's DOM output is handled via CSS, with relevant style classes stored in a CSS module file parallel to the widget's TypeScript module. Styling is identical for both function- and class-based widget variants. This topic is described in detail within the Styling and Theming reference guide.