DevExtreme React - Templates

Templates allow you to customize DevExtreme widgets. This article gives an overview of the capabilities that DevExtreme provides for implementing and applying templates.

NOTE
For documentation on templates in DevExtreme ASP.NET MVC Controls, refer to Implementing Templates (for ASP.NET MVC 5) or Templates (for ASP.NET Core).

Default Templates

Default templates are based on data source fields. You can control appearance by adding or removing particular fields from data source objects. For example, the List widget has a default template for items that contains the text, visible, and disabled fields. If you assign the following array to the widget's dataSource option, the first item will be disabled, the second hidden, both of them will have text, and the third item will render a custom markup:

JavaScript
function customMarkup() {
    var d = document.createElement("div");
    d.innerHTML = "<i>Oranges</i>";
    return d;
}
var fruits = [
    { text: "Apples", disabled: true },
    { text: "Lemons", visible: false },
    { template: customMarkup }
];

You can achieve the same in the markup using the dxItem component that supports default and custom templates. Do not set the widget's dataSource option in this case.

jQuery
HTML
JavaScript
<div id="list">
    <div data-options="dxItem: { text: 'Apples', disabled: true }"></div>
    <div data-options="dxItem: { text: 'Lemons', visible: false }"></div>
    <div data-options="dxItem: { }">
        <i>Oranges</i>
    </div>
</div>
$(function() {
    $("#list").dxList({/* ... */});
});
Angular
app.component.html
app.module.ts
<dx-list>
    <dxi-item text="Apples" [disabled]="true"></dxi-item>
    <dxi-item text="Lemons" [visible]="false"></dxi-item>
    <dxi-item>
        <i>Oranges</i>
    </dxi-item>
</dx-list>
import { DxListModule } from "devextreme-angular";
// ...
@NgModule({
    imports: [
        // ...
        DxListModule
    ],
    // ...
})
export class AppModule { }
AngularJS
HTML
JavaScript
<div dx-list="{ }">
    <div data-options="dxItem: { text: 'Apples', disabled: true }"></div>
    <div data-options="dxItem: { text: 'Lemons', visible: false }"></div>
    <div data-options="dxItem: { }">
        <i>Oranges</i>
    </div>
</div>
angular.module('DemoApp', ['dx'])
    .controller('DemoController', function ($scope) {
        // ...
    });
Knockout
HTML
JavaScript
<div data-bind="dxList: { ... }">
    <div data-options="dxItem: { text: 'Apples', disabled: true }"></div>
    <div data-options="dxItem: { text: 'Lemons', visible: false }"></div>
    <div data-options="dxItem: { }">
        <i>Oranges</i>
    </div>
</div>
var viewModel = {
    // ...
};

ko.applyBindings(viewModel);
Vue
App.vue
<template>
    <DxList>
        <DxItem text="Apples" :disabled="true" />
        <DxItem text="Lemons" :visible="false" />
        <DxItem>
            <template #default>
                <i>Oranges</i>
            </template>
        </DxItem>
    </DxList>
</template>

<script>
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import DxList, {
    DxItem
} from 'devextreme-vue/list';

export default {
    components: {
        DxList,
        DxItem
    }
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import List, { Item } from 'devextreme-react/list';

class App extends React.Component {
    render() {
        return (
            <List>
                <Item text="Apples" disabled={true} />
                <Item text="Lemons" visible={false} />
                <Item>
                    <i>Oranges</i>
                </Item>
            </List>
        );
    }
}
export default App;

Default templates and the fields available in them depend on the widget. For a list of default templates for a widget, search for "default template" in the left menu.

Custom Templates

DevExtreme provides the dxTemplate markup component for Angular, AngularJS, and Knockout apps.

Implement a dxTemplate within the widget's container using the syntax of the used library (Angular, Knockout, etc.). You can access a template's context properties within the template and, in item templates, the item index as shown in the following code. Assign the template's name to a widget's ...Template option. In the code below, it is the List widget's itemTemplate option. One widget can have multiple dxTemplates.

If you use jQuery alone, set the widget's ...Template option to a function that combines the HTML markup using jQuery DOM manipulation methods.

Angular
HTML
TypeScript
<dx-list ...
    [dataSource]="listData"
    itemTemplate="listItem">
    <div *dxTemplate="let itemData of 'listItem'; let itemIndex = index">
        {{itemIndex}} - {{itemData.itemProperty}}
    </div>
</dx-list>
import { DxListModule } from "devextreme-angular";
// ...
export class AppComponent {
    listData = [{ 
        itemProperty: "someValue",
        // ...
    },
    // ...
    ]
}
@NgModule({
    imports: [
        // ...
        DxListModule
    ],
    // ...
})
Vue
App.vue
<template>
    <DxList ...
        :data-source="listData"
        itemTemplate="item">
        <template #item="{ data, index }">
            {{ index }} - {{ data.itemProperty }}
        </template>
    </DxList>
</template>

<script>
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import DxList from 'devextreme-vue/list';

export default {
    components: {
        DxList
    },
    data() {
        return {
            listData: [
                { itemProperty: 'someValue' },
                // ...
            ]
        }
    }
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import List from 'devextreme-react/list';

const renderListItem = (itemData, itemIndex) => {
    return (
        <span>{ itemIndex } - { itemData.name }</span>
    );
}

class App extends React.Component {
    constructor(props) {
        super(props);
        this.listData = [
            { itemProperty: 'someValue' },
            // ...
        ];
    }
    render() {
        return (
            <List
                dataSource={this.listData}
                itemRender={renderListItem}
            />
        );
    }
}
export default App;
AngularJS
HTML
JavaScript
<div ng-controller="DemoController">
    <div dx-list="{
        ...
        dataSource: listData,
        itemTemplate: 'listItem'
    }" dx-item-alias="itemData">
        <div data-options="dxTemplate: { name: 'listItem' }">
            {{$index}} - {{itemData.itemProperty}}
        </div>
    </div>
</div>
angular.module('DemoApp', ['dx'])
    .controller('DemoController', function ($scope) {
        $scope.listData = [{ 
            itemProperty: "someValue",
            // ...
        },
        // ...
        ];
    });
Knockout
HTML
JavaScript
<div data-bind="dxList: {
    ...
    dataSource: listData,
    itemTemplate: 'listItem'
}">
    <div data-options="dxTemplate: { name: 'listItem' }">
        <span data-bind="text: $index"></span> - <span data-bind="text: itemProperty"></span>
    </div>
</div>
var viewModel = {
    listData: [{ 
        itemProperty: "someValue",
        // ...
    },
    // ...
    ]
};

ko.applyBindings(viewModel);
jQuery
JavaScript
$(function() {
    $("#listContainer").dxList({
        dataSource: [{ 
            itemProperty: "someValue",
            // ...
        },
        // ...
        ],
        itemTemplate: function (itemData, itemIndex, element) {
            element.append(
                $("<span>").text(itemIndex) - $("<span>").text(itemData.itemProperty)
            )
        }
    });
});

View Demo

You can define custom templates for individual items in collection widgets. When using any library or framework except jQuery, declare the items using the dxItem component as shown in the following code. Do not set the widget's dataSource option. To do the same with jQuery, assign a function combining the HTML markup to a data source object's template option.

Angular
HTML
TypeScript
<dx-list ... >
    <dxi-item>
        <i>Item 1</i>
    </dxi-item>
    <dxi-item>
        <b>Item 2</b>
    </dxi-item>
    <dxi-item>
        Item with a nested component
        <div *dxTemplate>
            <dx-button text="Click me"></dx-button>
        </div>
    </dxi-item>
</dx-list>
import { DxListModule, DxButtonModule } from "devextreme-angular";
// ...
export class AppComponent {
    // ...
}
@NgModule({
    imports: [
        // ...
        DxListModule,
        DxButtonModule
    ],
    // ...
})
Vue
App.vue
<template>
    <DxList>
        <DxItem>
            <template #default>
                <i>Item 1</i>
            </template>
        </DxItem>
        <DxItem>
            <template #default>
                <i>Item 2</i>
            </template>
        </DxItem>
        <DxItem>
            Item with a nested component
            <template #default>
                <div>
                    <DxButton text="Click me" />
                </div>
            </template>
        </DxItem>
    </DxList>
</template>

<script>
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import DxList, {
    DxItem
} from 'devextreme-vue/list';
import DxButton from 'devextreme-vue/button';

export default {
    components: {
        DxList,
        DxItem,
        DxButton
    }
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import List, { Item } from 'devextreme-react/list';
import Button from 'devextreme-react/button';

class App extends React.Component {
    render() {
        return (
            <List>
                <Item>
                    <i>Item 1</i>
                </Item>
                <Item>
                    <i>Item 2</i>
                </Item>
                <Item>
                    <Button text="Click me" />
                </Item>
            </List>
        );
    }
}
export default App;
AngularJS
HTML
JavaScript
<div ng-controller="DemoController">
    <div dx-list="{ ... }">
        <div data-options="dxItem: { }">
            <i>Item 1</i>
        </div>
        <div data-options="dxItem: { }">
            <b>Item 2</b>
        </div>
        <div data-options="dxItem: { }">
            Item with a nested component
            <div dx-button="{
                text: 'Click me'
            }"></div>
        </div>
    </div>
</div>
angular.module('DemoApp', ['dx'])
    .controller('DemoController', function ($scope) {
        // ...
    });
Knockout
HTML
JavaScript
<div data-bind="dxList: { ... }">
    <div data-options="dxItem: { }">
        <i>Item 1</i>
    </div>
    <div data-options="dxItem: { }">
        <b>Item 2</b>
    </div>
    <div data-options="dxItem: { }">
        Item with a nested component
        <div data-bind="dxButton: {
            text: 'Click me'
        }"></div>
    </div>
</div>
var viewModel = {
    // ...
};

ko.applyBindings(viewModel);
jQuery
JavaScript
$(function() {
    $("#listContainer").dxList({
        dataSource: [{
            template: function () {
                return $("<i>").text("Item 1")
            }
        }, {
            template: function () {
                return $("<b>").text("Item 2")
            }
        },{
            template: function () {
                return $("<div>").append(
                    $("<span>").text("Item with nested component"),
                    $("<div>").dxButton({
                        text: "Click me"
                    })
                )
            }
        }]
    });
});

Many widgets provide options that end with ...Template, but particular widgets (Button, Drawer, and others) have an option called template. These widgets allow you to declare the markup directly in the widget element to specify their content.

Angular
app.component.html
app.component.ts
app.module.ts
<dx-button (onClick)="foo($event)">
    <i style="color:green">
        Refresh
    </i>
</dx-button>
// ...
export class AppComponent {
    foo(e) {
        // ...
    }
}
import { DxButtonModule } from "devextreme-angular";
// ...
@NgModule({
    imports: [
        // ...
        DxButtonModule
    ],
    // ...
})
Vue
App.vue
<template>
    <DxButton @click="foo">
        <i style="color:green">
            Refresh
        </i>
    </DxButton>
</template>

<script>
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import DxButton from 'devextreme-vue/button';

export default {
    components: {
        DxButton
    },
    methods: {
        foo(e) {
            // ...
        }
    }
}
</script>
React
App.js
import React from 'react';
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';

import Button from 'devextreme-react/button';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.foo = (e) => {
            // ...
        }
    }
    render() {
        return (
            <Button
                onClick={this.foo}>
                <i style={{ color: 'green' }}>
                    Refresh
                </i>
            </Button>
        );
    }
}
export default App;
jQuery
HTML
JavaScript
<div id="buttonContainer">
    <i style="color:green">
        Refresh
    </i>
</div>
$(function() {
    $("#buttonContainer").dxButton({
        onClick: function(e) {
            // ...
        }
    });
});

3rd-Party Template Engines

You can use a 3rd-party template engine, but only with jQuery. DevExtreme supports the following template engines out of the box:

To use one of them, pass its name to the DevExpress.ui.setTemplateEngine(name) method:

JavaScript
HTML
DevExpress.ui.setTemplateEngine("underscore");

$(function() {
    $("#list").dxList({
        // ...
        itemTemplate: $("#itemTemplate")
    });
})
<div id="list"></div>
<script type="text/html" id="itemTemplate">
    <!-- your Underscore template -->
</script>

View Demo

You can also use other template engines, but you need to implement functions that compile and render templates in this case. See DevExpress.ui.setTemplateEngine(options) for details.