DevExtreme React - Component Configuration Syntax

To help you configure DevExtreme UI components in a React-style code structure, we implemented configuration components. These are elements nested in JSX code as if they represented a UI component's sub-elements, but from the technical point of view, they only carry the configuration data. This means that UI component properties can be supplied declaratively and selectively, with default fallbacks in place.

Static Property Value

Function component
Class component
import Button from 'devextreme-react/button';

export default function App() {
    return (
        <Button
            disabled={false}
            width={50}
            text="Click me"
        />
    );
}
import Button from 'devextreme-react/button';

class App extends React.Component {
    render() {
        return (
            <Button
                disabled={false}
                width={50}
                text="Click me"
            />
        );
    }
}

Properties of the Object Type

Use nested configuration components. In the following example, we configure the Chart UI component's tooltip property:

Function component
Class component
import Chart, {
    Tooltip
} from 'devextreme-react/chart';

export default function App() {
    return (
        <Chart>
            <Tooltip
                enabled={true}
                format="thousands"
            />
        </Chart>
    );
}
import Chart, {
    Tooltip
} from 'devextreme-react/chart';

class App extends React.Component {
    render() {
        return (
            <Chart>
                <Tooltip
                    enabled={true}
                    format="thousands"
                />
            </Chart>
        );
    }
}

Object type properties that depend on other properties' values are not implemented as nested configuration components because they cannot be typed (columns[].editorOptions in the DataGrid, item's editorOptions in the Form, items[].options in the Toolbar). These properties should be specified with an object.

Function component
Class component
import DataGrid, {
    Column
} from 'devextreme-react/data-grid';

const columnEditorOptions = { width: 100 };

export default function App() {
    return (
        <DataGrid>
            <Column
                editorOptions={columnEditorOptions}
            />
        </DataGrid>
    );
}
import DataGrid, {
    Column
} from 'devextreme-react/data-grid';

class App extends React.Component {
    columnEditorOptions = { width: 100 };

    render() {
        return (
            <DataGrid>
                <Column
                    editorOptions={this.columnEditorOptions}
                />
            </DataGrid>
        );
    }
}
IMPORTANT
We recommend that you declare the object outside the configuration component to prevent possible issues caused by unnecessary re-rendering.

If you use React Hooks and need to define a configuration object inside a function component, wrap this object in the useMemo hook to preserve the object's reference between state changes:

App.js
import React, { useState, useMemo } from 'react';
import Form, { Label, SimpleItem } from 'devextreme-react/form';

const data = { isAddressRequired: false, Address: '' };

export default function App() {
    const [visible, setVisible] = useState(false);
    const checkBoxOptions = useMemo(() => {
        return {
            text: "Display Address",
            onValueChanged: (e) => {
                setVisible(e.value);
            }
        }
    }, []);
    const addressOptions = useMemo(() => {
        return {
            placeholder: 'Enter your addresss',
            maxLength: 50
        }
    }, []);

    return (
        <Form formData={data}
            width={400}>
            <SimpleItem
                dataField="isAddressRequired"
                editorType="dxCheckBox"
                editorOptions={checkBoxOptions}>
                <Label visible={false} />
            </SimpleItem>
            <SimpleItem
                dataField="Address"
                editorType="dxTextBox"
                editorOptions={addressOptions}
                visible={visible}
            />
        </Form>
    );
}
See Also

Collections

Use nested configuration components. The following example shows how to configure the DataGrid's columns property:

Function component
Class component
import DataGrid, {
    Column
} from 'devextreme-react/data-grid';

export default function App() {
    return (
        <DataGrid>
            <Column
                dataField="firstName"
                caption="First Name"
            />
            <Column
                dataField="lastName"
                caption="Last Name"
                defaultVisible={true}
            />
        </DataGrid>
    );
}
import DataGrid, {
    Column
} from 'devextreme-react/data-grid'; 

class App extends React.Component {
    render() {
        return (
            <DataGrid>
                <Column
                    dataField="firstName"
                    caption="First Name"
                />
                <Column
                    dataField="lastName"
                    caption="Last Name"
                    defaultVisible={true}
                />
            </DataGrid>
        );
    }
}

DevExtreme collection UI components also support the Item element. It allows you to declare collection items in the UI component markup. An Item element can contain custom markup and have properties that control parts of item appearance, such as badge in the following code. The properties are described in the items section of each collection UI component.

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

export default function App() {
    return (
        <List>
            <Item>Orange</Item>
            <Item badge="New">White</Item>
            <Item>Black</Item>
        </List>
    );
}
import List, {
    Item
} from 'devextreme-react/list';

class App extends React.Component {
    render() {
        return (
            <List>
                <Item>Orange</Item>
                <Item badge="New">White</Item>
                <Item>Black</Item>
            </List>
        );
    }
}
See Also

Event Handling

Function component
Class component
import { useCallback } from 'react';
import Button from 'devextreme-react/button';

export default function App() {
    const handleButtonClick = useCallback((e) => {
        alert("The button was clicked")
    }, []);

    return (
        <Button
            onClick={handleButtonClick}
        />
    );
}
import Button from 'devextreme-react/button';

class App extends React.Component {
    constructor(props) {
        super(props);
        // Uncomment the line below to bind the handler to the React component's context, for example, to call this.setState()
        // this.handleButtonClick = this.handleButtonClick.bind(this);
    }

    render() {
        return (
            <Button
                onClick={this.handleButtonClick}
            />
        );
    }

    handleButtonClick(e) {
        alert("The button was clicked")
    }
}
IMPORTANT
In function components, wrap the event handler declaration into the useCallback React Hook to prevent possible issues caused by unnecessary re-rendering.

Callback Functions

Function component
Class component
import { useCallback } from 'react';
import VectorMap, { Layer } from 'devextreme-react/vector-map';

export default function App() {
    const customizeLayers = useCallback((elements) => {
        // ...
    }, []);

    return (
        <VectorMap>
            <Layer
                customize={customizeLayers}
            />
        </VectorMap>
    );
}
import VectorMap, { Layer } from 'devextreme-react/vector-map';

class App extends React.Component {
    render() {
        return (
            <VectorMap>
                <Layer
                    customize={this.customizeLayers}
                />
            </VectorMap>
        );
    }

    customizeLayers(elements) {
        // ...
    }
}
IMPORTANT
In function components, wrap the callback function declaration into the useCallback React Hook to prevent possible issues caused by unnecessary re-rendering.

In class components, callback functions are executed outside the React component's context. If the context is important, explicitly bind the callback function to it in the constructor.

Class component
class App extends React.Component {
    myCountry: string = 'USA'; // we need to access this context variable in the callback function

    constructor(props) {
        super(props);
        this.customizeLayers = this.customizeLayers.bind(this);
    }

    customizeLayers(elements) {
        let country = this.myCountry;
        // ...
    }
    // ...        
}

Declare Content in the Markup

The following UI components allow you to declare their content directly in the markup:

The following is an example with ScrollView:

Function component
Class component
import ScrollView from 'devextreme-react/scroll-view';

export default function App() {
    return (
        <ScrollView>
            <div>Some scrollable content</div>
        </ScrollView>
    );
}
import ScrollView from 'devextreme-react/scroll-view';

class App extends React.Component {
    render() {
        return (
            <ScrollView>
                <div>Some scrollable content</div>
            </ScrollView>
        );
    }
}
IMPORTANT

These UI components do not support dynamically or conditionally rendered content in their root element. For example, the following code does not work:

App.js
<Drawer ... >
    {
        someCondition && <div> ... </div>
        // when the condition changes in runtime, the UI component may not render content correctly
    }
</Drawer>

Wrap the content in a static element:

App.js
<Drawer ... >
    <div>
        {
            someCondition && <div> ... </div>
        }
    </div>
</Drawer>

Note that React.Fragment is a dynamically rendered element and doesn't fit in this case. Use static elements such as div or span instead.

Markup Customization

You can display custom content in DevExtreme components. We supply two types of properties for this task: those that end with 'Render' (for example, cellRender), and those that end with 'Component' (for example, cellComponent).

'Render' properties allow you to employ rendering functions. A DevExpress control calls the specified function and renders the return value to the virtual DOM. Note that a rendering function is not a component: it lacks state and cannot utilize hooks. However, you can create a rendering function inside your component and enclose it within the useCallback hook. You can pass local variables as dependencies and use them in your template. The following article contains an example: Using a Rendering Function.

'Component' properties use React components to specify templates. A DevExpress control renders those templates to the virtual DOM. Since a template is a component, it can have its internal state and use hooks inside (which is not possible with rendering functions). The following article contains an example: Using a Custom Component.

In some cases, you need to use the 'Template' properties instead. Refer to the following article for more information: Using the Template Component.

Using a Rendering Function

The following example customizes a DataGrid component to display a combination of grid data and a TextBox value within cells. The code wraps a rendering function within a useCallback hook. This way you can pass the TextBox value to the column's cellRender property.

NOTE
You can also define a rendering function outside of a component if you don't need to pass any dependencies to the 'Render' property.
Function component
Class component
import React, { useState, useCallback } from 'react';
import TextBox from 'devextreme-react/text-box';
import DataGrid, { Column } from 'devextreme-react/data-grid';

const dataSource = [1, 2, 3, 4, 5];

function App() {
    const [value, setValue] = useState('.');

    const onTextBoxValueChange = (e) => {
        setValue(e);
    };

    const cell = useCallback((cell) => <div>{cell.data + value}</div>, [value]);

    return (
        <>
            <TextBox value={value} onValueChange={onTextBoxValueChange} />
            <DataGrid dataSource={dataSource}>
                <Column caption="Low" cellRender={cell} />
            </DataGrid>
        </>
    );
}

export default App;
import * as React from 'react';
import TextBox from 'devextreme-react/text-box';
import DataGrid, { Column } from 'devextreme-react/data-grid';

const dataSource = [1, 2, 3, 4, 5];

class App extends React.Component {
    constructor() {
        super();

        this.renderCell = this.renderCell.bind(this);
        this.onTextBoxValueChange = this.onTextBoxValueChange.bind(this);

        this.state = { value: '.' };
    }

    onTextBoxValueChange(e) {
        this.setState({ value: e });
    };

    renderCell(cell) {
        return <div>{cell.data + this.state.value}</div>;
    }

    render() {
        return (
            <>
                <TextBox value={this.state.value} onValueChange={this.onTextBoxValueChange} />
                <DataGrid dataSource={dataSource}>
                    <Column caption="Low" cellRender={this.renderCell} />
                </DataGrid>
            </>
        );
    }
}

export default App;

Using a Custom Component

You can define custom content in a separate component if you need to utilize the state or hooks.

NOTE
For Class components, we recommend that you use React.PureComponent instead of React.Component (the latter can be re-rendered unnecessarily). Alternatively, you can implement a shouldComponentUpdate() method.

In the following code snippet, a standalone ListItem component is created to render List items. This component specifies the itemComponent property value. If necessary, you can reuse this ListItem component in another List.

Function component
Class component
import React, { useCallback, useState } from "react";
import List from "devextreme-react/list";

import "devextreme/dist/css/dx.light.css";

const dataSource = ["Apples", "Bananas", "Cranberries"];
const defaultWeight = 1;

const ListItem = ({ data }) => {
    const [weight, setWeight] = useState(defaultWeight);
    const onWeightChange = useCallback(
        (e) => setWeight(e.target.value || defaultWeight),
        []
    );

    return (
        <div
            style={{
                display: "flex",
                flexDirection: "row",
                justifyContent: "space-between",
                alignItems: "center",
            }}
        >
            <span>{`${data}, ${weight} lb`}</span>
            <input type="number" placeholder="Weight" onChange={onWeightChange} />
        </div>
    );
};

function App() {
    return (
        <div style={{ maxWidth: 400 }}>
            <List
                activeStateEnabled={false}
                items={dataSource}
                itemComponent={ListItem}
            />
        </div>
    );
}

export default App;
import * as React from 'react';
import List from 'devextreme-react/list';

import 'devextreme/dist/css/dx.light.css';

const dataSource = ['Apples', 'Bananas', 'Cranberries'];
const defaultWeight = 1;

class ListItem extends React.PureComponent {
    constructor() {
        super();

        this.onWeightChange = this.onWeightChange.bind(this);
        this.state = { weight: defaultWeight };
    }

    onWeightChange(e) {
        this.setState({ weight: e.target.value || defaultWeight });
    }

    render() {
        return (
            <div style={{
                display: 'flex',
                flexDirection: 'row',
                justifyContent: 'space-between',
                alignItems: 'center',
            }}>
                <span>{`${this.props.data}, ${this.state.weight} lb`}</span>
                <input
                    type='number'
                    placeholder='Weight'
                    onChange={this.onWeightChange}
                />
            </div>
        );
    }
}

class App extends React.Component {
    render() {
        return (
            <div style={{ maxWidth: 400 }}>
                <List activeStateEnabled={false} items={dataSource} itemComponent={ListItem} />
            </div>
        );
    }
}

export default App;

Using the Template Component

Several properties are not implemented as nested configuration components (columns[].editorOptions in the DataGrid, item's editorOptions in the Form, items[].options in the Toolbar). These properties do not have a Render or Component property where you would pass your rendering function or custom component. However, you can still use the Template element to customize the markup.

The Template element declares a named template. Its name property value should be assigned to a Template property of the UI component. You can specify the template's markup as follows:

  • Rendering function
    Pass the rendering function to the Template's render property:

    Function component
    Class component
    data.js
    import React, { useCallback, useState } from 'react';
    import Form, { Item } from 'devextreme-react/form';
    import { Template } from 'devextreme-react/core/template';
    import service from './data.js';
    import { CheckBox } from 'devextreme-react/check-box';
    
    const employee = service.getEmployee();
    const positions = service.getPositions();
    const positionEditorOptions = {
        items: positions,
        value: '',
        itemTemplate: 'selectBoxItem'
    };
    
    export default function App() {
        const [upperCasePositions, setUpperCasePositions] = useState(false);
        const renderSelectBoxItem = useCallback((item) => {
            return <div>{upperCasePositions ? item.toUpperCase() : item}</div>;
        }, [upperCasePositions]);
        const onUpperCaseSettingChange = useCallback((e) => {
            setUpperCasePositions(e.value);
        }, []);
        return (
            <>
                <CheckBox
                    text="Display positions in upper case"
                    onValueChanged={onUpperCaseSettingChange}
                />
                <Form formData={employee}>
                    <Item
                        dataField="Position"
                        editorType="dxSelectBox"
                        editorOptions={positionEditorOptions}
                    />
                    <Template name="selectBoxItem" render={renderSelectBoxItem} />
                </Form>
            </>
        );
    }
    import * as React from 'react';
    import Form, { Item } from 'devextreme-react/form';
    import { Template } from 'devextreme-react/core/template';
    import { CheckBox } from 'devextreme-react/check-box';
    import service from './data.js';
    
    class App extends React.Component {
        constructor(props) {
            super(props);
            this.state = { upperCasePositions: false };
            this.employee = service.getEmployee();
            this.positions = service.getPositions();
            this.renderSelectBoxItem = this.renderSelectBoxItem.bind(this);
            this.onUpperCaseSettingChange = this.onUpperCaseSettingChange.bind(this);
            this.positionEditorOptions = {
                items: this.positions,
                value: '',
                itemTemplate: 'selectBoxItem'
            };
        }
        renderSelectBoxItem(item) {
            return <div>{this.state.upperCasePositions ? item.toUpperCase() : item}</div>;
        }
        onUpperCaseSettingChange(e) {
            this.setState({ upperCasePositions: e.value });
        }
        render() {
            return (
                <>
                <CheckBox
                    text="Display positions in upper case"
                    onValueChanged={this.onUpperCaseSettingChange}
                />
                <Form formData={this.employee}>
                    <Item
                        dataField="Position"
                        editorType="dxSelectBox"
                        editorOptions={this.positionEditorOptions}
                    />
                    <Template name="selectBoxItem" render={this.renderSelectBoxItem} />
                </Form>
            </>
            );
        }
    }
    
    export default App;
    const employee = {
        ID: 1,
        FirstName: 'John',
        LastName: 'Heart',
        Position: 'CEO',
        BirthDate: '1964/03/16',
        HireDate: '1995/01/15',
        Address: '351 S Hill St., Los Angeles, CA',
        Phone: '360-684-1334',
        Email: 'jheart@dx-email.com'
    };
    
    const positions = [
        'HR Manager',
        'IT Manager',
        'CEO',
        'Controller',
        'Sales Manager',
        'Support Manager',
        'Shipping Manager'
    ];
    
    export default {
        getEmployee() {
            return employee;
        },
        getPositions() {
            return positions;
        }
    }
  • Custom component
    Assign the custom component to the Template's component property:

    Function component
    Class component
    data.js
    import React, { useState, useCallback } from 'react';
    import Form, { Item } from 'devextreme-react/form';
    import { Template } from 'devextreme-react/core/template';
    import { Switch } from 'devextreme-react/switch';
    import { RadioGroup } from 'devextreme-react/radio-group';
    import service from './data.js';
    
    const employee = service.getEmployee();
    const positions = service.getPositions();
    
    function RadioGroupItemTemplate({ data }) {
        const [lineThrough, setLineThrough] = useState(false);
        const onLineThroughChange = useCallback((e) => {
            setLineThrough(e);
        }, []);
        return (
            <div style={{
                display: 'flex',
                flexDirection: 'row',
                justifyContent: 'space-between',
                alignItems: 'center',
            }}>
                <Switch onValueChange={onLineThroughChange}/>
                <div style={{
                    marginLeft: '10px',
                    textDecoration: lineThrough ? 'line-through' : 'none',
                }}>{data}</div>
            </div>
        );
    };
    
    function App() {
        const [positionEditorOptions] = useState({
            items: positions,
            value: '',
            itemTemplate: 'radioGroupItem',
        });
        return (
            <div style={{ maxWidth: 400 }}>
                <Form formData={employee}>
                    <Item
                        dataField="Position"
                        editorType="dxRadioGroup"
                        editorOptions={positionEditorOptions}
                    />
                    <Template name="radioGroupItem" component={RadioGroupItemTemplate} />
                </Form>
            </div>
        );
    };
    
    export default App;
    import * as React from 'react';
    import Form, { Item } from 'devextreme-react/form';
    import { Template } from 'devextreme-react/core/template';
    import { Switch } from 'devextreme-react/switch';
    import { RadioGroup } from 'devextreme-react/radio-group';
    import service from './data.js';
    
    class RadioGroupItemTemplate extends React.PureComponent {
        constructor() {
            super();
            this.state = { lineThrough: false };
            this.onLineThroughChange = this.onLineThroughChange.bind(this);
        }
        onLineThroughChange(e) {
            this.setState({ lineThrough: e });
        }
        render() {
            return (
                <div style={{
                    display: 'flex',
                    flexDirection: 'row',
                    justifyContent: 'space-between',
                    alignItems: 'center',
                }}>
                    <Switch onValueChange={this.onLineThroughChange}/>
                    <div style={{
                        marginLeft: '10px',
                        textDecoration: this.state.lineThrough ? 'line-through' : 'none',
                    }}>
                        {this.props.data}
                    </div>
                </div>
            );
        }
    }
    
    class App extends React.Component {
        constructor(props) {
            super(props);
            this.employee = service.getEmployee();
            this.positions = service.getPositions();
            this.positionEditorOptions = {
                items: this.positions,
                value: '',
                itemTemplate: 'radioGroupItem'
            };
        }
        render() {
            return (
                <div style={{ maxWidth: 400 }}>
                    <Form formData={this.employee}>
                        <Item
                            dataField="Position"
                            editorType="dxRadioGroup"
                            editorOptions={this.positionEditorOptions}
                        />
                        <Template name="radioGroupItem" component={RadioGroupItemTemplate} />
                    </Form>
                </div>
            );
        }
    }
    
    export default App;
    const employee = {
        ID: 1,
        FirstName: 'John',
        LastName: 'Heart',
        Position: 'CEO',
        BirthDate: '1964/03/16',
        HireDate: '1995/01/15',
        Address: '351 S Hill St., Los Angeles, CA',
        Phone: '360-684-1334',
        Email: 'jheart@dx-email.com'
    };
    
    const positions = [
        'HR Manager',
        'IT Manager',
        'CEO',
        'Controller',
        'Sales Manager',
        'Support Manager',
        'Shipping Manager'
    ];
    
    export default {
        getEmployee() {
            return employee;
        },
        getPositions() {
            return positions;
        }
    }

Call Methods

To call UI component methods, you need the UI component instance. Create a ref and attach it to the target component via the ref property. In the following code, this approach is used to get a TextBox instance:

Function component
Class component
import React, { useRef, useCallback } from 'react';
import Button from 'devextreme-react/button';
import TextBox from 'devextreme-react/text-box';

export default function App() {
    const textBox = useRef(null);
    const focusTextBox = useCallback(() => {
        // `current.instance()` points to the UI component instance 
        textBox.current.instance().focus();
    }, []);

    return (
        <div>
            <TextBox ref={textBox} />
            <Button text="Focus TextBox" onClick={focusTextBox} />
        </div>
    );
}
import Button from 'devextreme-react/button';
import TextBox from 'devextreme-react/text-box';

class App extends React.Component {
    constructor(props) {
        super(props);

        this.textBoxRef = React.createRef();

        this.focusTextBox = () => {
            this.textBox.focus()
        };
    }

    get textBox() {
        // `current.instance()` points to the UI component instance
        return this.textBoxRef.current.instance();
    }

    render() {
        return (
            <div>
                <TextBox ref={this.textBoxRef} />
                <Button text="Focus TextBox" onClick={this.focusTextBox} />
            </div>
        );
    }
}

Alternatively, you can assign the UI component instance to a variable and use it to call the methods. This approach is not compatible with React Hooks.

Class component
import Button from 'devextreme-react/button';
import TextBox from 'devextreme-react/text-box';

class App extends React.Component {
    constructor(props) {
        super(props);

        this.saveTextBoxInstance = this.saveTextBoxInstance.bind(this);
        this.focusTextBox = this.focusTextBox.bind(this);
    }

    saveTextBoxInstance(e) {
        this.textBoxInstance = e.component;
    }

    focusTextBox() {
        this.textBoxInstance.focus();
    }

    render() {
        return (
            <div>
                <TextBox onInitialized={this.saveTextBoxInstance} />
                <Button text="Focus TextBox" onClick={this.focusTextBox} />
            </div>
        );
    }
}

Get a UI Component Instance

For information on this matter, refer to Call Methods.

Data Layer

DevExtreme Data Layer is a set of components for working with data. The following example shows how to use the DataSource component with the List UI component:

Function component
Class component
import React, { useEffect } from 'react';
import DataSource from 'devextreme/data/data_source';
import List from 'devextreme-react/list';

const items = [
    { text: '123' },
    { text: '234' },
    { text: '567' }
];
const dataSource = new DataSource({
    store: {
        type: "array",
        data: items
    },
    sort: { getter: "text", desc: true }
});

export default function App() {
    useEffect(() => {
        // A DataSource instance created outside a UI component should be disposed of manually.
        return () => { dataSource.dispose(); }
    });
    return (
        <List dataSource={dataSource} />
    ); 
}
import DataSource from 'devextreme/data/data_source';
import List from 'devextreme-react/list';

const items = [
    { text: '123' },
    { text: '234' },
    { text: '567' }
];

class Example extends React.Component {
    constructor(props) {
        super(props);

        this.dataSource = new DataSource({
            store: {
                type: 'array',
                data: items
            },
            sort: { getter: 'text', desc: true }
        });
    }

    render() {
        return (
            <List dataSource={this.dataSource} />
        );
    }

    componentWillUnmount() {
        // A DataSource instance created outside a UI component should be disposed of manually.
        this.dataSource.dispose();
    }
}
NOTE
When a data layer component's properties are modified, the bound UI component is not re-rendered.

DevExtreme Validation Features

In the following example, two textboxes are placed in a validation group that is validated on a button click. Each textbox has a set of validation rules. The validation result is displayed under the textboxes in a validation summary.

Function component
Class component
import React, { useState, useCallback } from 'react';
import TextBox from 'devextreme-react/text-box';
import Validator, { RequiredRule, EmailRule } from 'devextreme-react/validator';
import ValidationGroup from 'devextreme-react/validation-group';
import Button from 'devextreme-react/button';

export default function App() {
    const [email, setEmail] = useState(null);
    const [password, setPassword] = useState(null);
    const validate = useCallback((params) => {
        const result = params.validationGroup.validate();
        if (result.isValid) {
            // The values are valid
            // Submit them...
            // ...
            // ... and then reset
            // params.validationGroup.reset();
        }
    }, []);
    const handleEmailChange = useCallback((e) => {
        setEmail(e.value);
    }, []);
    const handlePasswordChange = useCallback((e) => {
        setPassword(e.value);
    }, []);

    return (
        <ValidationGroup>
            <TextBox value={email} onValueChanged={handleEmailChange}>
                <Validator>
                    <RequiredRule message="Email is required" />
                    <EmailRule message="Email is invalid" />
                </Validator>
            </TextBox>

            <TextBox value={password} mode="password" onValueChanged={handlePasswordChange}>
                <Validator>
                    <RequiredRule message="Password is required" />
                </Validator>
            </TextBox>
            <Button onClick={validate} text="Submit" />
        </ValidationGroup>
    ); 
}
import TextBox from 'devextreme-react/text-box';
import Validator, { RequiredRule, EmailRule } from 'devextreme-react/validator';
import ValidationGroup from 'devextreme-react/validation-group';
import Button from 'devextreme-react/button';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            email: null,
            password: null
        };
        this.handleEmailChange = (e) => {
            this.setState({
                email: e.value
            });
        };
        this.handlePasswordChange = (e) => {
            this.setState({
                password: e.value
            });
        };
        this.validate = this.validate.bind(this);
    }

    render() {
        return (
            <ValidationGroup>
                <TextBox value={this.state.email} onValueChanged={this.handleEmailChange}>
                    <Validator>
                        <RequiredRule message="Email is required" />
                        <EmailRule message="Email is invalid" />
                    </Validator>
                </TextBox>

                <TextBox value={this.state.password} mode="password" onValueChanged={this.handlePasswordChange}>
                    <Validator>
                        <RequiredRule message="Password is required" />
                    </Validator>
                </TextBox>
                <Button onClick={this.validate} text="Submit" />
            </ValidationGroup>
        );
    }

    validate(params) {
        const result = params.validationGroup.validate();
        if (result.isValid) {
            // The values are valid
            // Submit them...
            // ...
            // ... and then reset
            // params.validationGroup.reset();
        }
    }
}

Refer to the Data Validation article for more information.

Custom Nested Configuration Components

You can reuse parts of your application with custom nested configuration components.

For example, you can wrap common settings and reuse them in different DataGrids:

const CommonSettings = () => {
    return (
        <>
            <GroupPanel visible={true} />
            <SearchPanel visible={true} highlightCaseSensitive={true} />
            <Grouping autoExpandAll={false} />
        </>
    );
};

const MyGrid = () => (
    <DataGrid>
        <CommonSettings />
        <Column dataField="SaleDate" dataType="date" />
        <Column dataField="Product" />
    </DataGrid>
);
IMPORTANT

Avoid creating custom nested configuration components that may update separately from the parent DevExtreme component.

React components that rely on high-level React context or whose state can change independently from the parent component can usually be updated even if a parent component does not re-render.

In custom nested configuration components, such updates can be applied only after the parent re-renders for the second time, causing unexpected behavior.

The following code snippet illustrates this problematic approach with pager settings. Avoid such implementations.

const PagerSettings = () => {
    const pageSizes = useContext(PageSizeContext);
    return (
        <>
            <Pager allowedPageSizes={pageSizes} showPageSizeSelector={true} />
            <Paging defaultPageSize={pageSizes[0]} />
        </>
    );
};

const MyGrid = () => (
    <DataGrid>
        <PagerSettings />
        <Column dataField="Region" dataType="string" />
        <Column dataField="Customer" dataType="string" width={150} />
    </DataGrid>
);