Components
react-tree-multi-select allows you to customize its built-in components by providing your own custom components as a property. This feature enables you to tailor the appearance and behavior of various components.
The general pattern for implementing a custom component involves creating a component and passing the attributes prop to the root element. Here's a simple implementation:
const CustomChipLabel: FC<ChipLabelProps> = (props) => (
<div {...props.attributes}>
{/*your custom code here*/}
</div>
);
IMPORTANTYou must pass attributes prop to the root element in order for component to work properly.
If you would like to add custom CSS class to component, you can merge component classname with your own like in the example below:
const CustomChipLabel: FC<ChipLabelProps> = (props) => (
<div {...props.attributes} className={`${props.attributes.className} custom-classname`}>
{/*your custom code here*/}
</div>
);
Built-in components
You can import and reuse built-in components from the library for additional flexibility (except of Footer, since it has empty implementation by default). For an example, refer to the CustomChipContainer.const CustomChipContainer: FC<ChipContainerProps> = (props) => (
<Tooltip content={`Tooltip for the ${props.ownProps.label}`}>
<components.ChipContainer {...props}/>
</Tooltip>
);
Custom props
You can pass your own properties to the component through the props object. When creating a custom component, you can access these properties and use them in your implementation.
IMPORTANTTo improve performance, make sure to memoize components prop (and other props like objects, arrays, or callbacks). This helps prevent unnecessary re-renders by ensuring prop references remain stable between renders.
export const CustomExample: FC = () => {
const data = useMemo(() => getTreeNodeData(true), []);
const components: Components = useMemo(() => (
{
ChipLabel: {
component: CustomChipLabel,
props: {suffix: 'Yo'}
}
}
), []);
return (
<div className="component-example">
<TreeMultiSelect
data={data}
components={components}
/>
</div>
);
};
For an example, refer to the CustomChipLabel.
TypeScript support
You can use generic parameters to strongly type each component, ensuring type safety and preventing accidental misuse.
NOTE- Component type entries can be listed in any order. The order of keys in the generic argument does not matter — TypeScript will correctly match each one by name.
- Only known component keys (Field, Footer, NodeContainer, etc.) are allowed.
- Each component’s props are strongly typed according to the generics you specify.
- Extra or unknown keys (like WrongComponent) or mismatched types will result in a TypeScript error.
- Extra or unknown fields inside the component config (like wrongProp) are disallowed.
const createComponents = (label: string): Components<{ Field: FieldType<CustomFieldProps>; }> => ({
Field: {
component: CustomField,
props: {label},
},
});
For an example, refer to the CustomField.
Field
Let's say you have filters on a page that, when clicked, open a dropdown with a range of filter options.
It can be achieved by providing custom Field component like in the example below.
IMPORTANTYour custom Field component must either be focusable itself or contain exactly one focusable child to ensure proper keyboard navigation.
import React, {FC, useMemo} from 'react';
import {Components, FieldProps, FieldType, TreeMultiSelect, Type} from 'react-tree-multi-select';
interface CustomFieldProps {
label: string;
}
const CustomField: FC<FieldProps<CustomFieldProps>> = (props) => (
<div {...props.attributes}>
<button className="filter-btn">{props.customProps.label}</button>
</div>
);
export const CustomFieldExample: FC = () => {
const createComponents = (label: string): Components<{ Field: FieldType<CustomFieldProps>; }> => ({
Field: {
component: CustomField,
props: {label},
},
});
const companyComponents = useMemo(() => createComponents('Filter by company'), []);
const brandComponents = useMemo(() => createComponents('Filter by brand'), []);
const priceComponents = useMemo(() => createComponents('Filter by price'), []);
return (
<div className="component-example field-example">
<TreeMultiSelect
data={[
{
label: 'Company1',
children: [{label: 'Company1Branch1'}, {label: 'Company1Branch2'}],
expanded: true
},
{
label: 'Company2',
children: [{label: 'Company2Branch1'}, {label: 'Company2Branch2', selected: true}],
expanded: true
},
{
label: 'Company3',
children: [{label: 'Company3Branch1', disabled: true}, {label: 'Company3Branch2'}],
expanded: true
}
]}
withDropdownInput={true}
components={companyComponents}
/>
<TreeMultiSelect
type={Type.MULTI_SELECT}
data={[
{label: 'Brand1'},
{label: 'Brand2'},
{label: 'Brand3', selected: true},
{label: 'Brand4'},
{label: 'Brand5', selected: true}
]}
withSelectAll={true}
components={brandComponents}
/>
<TreeMultiSelect
type={Type.SELECT}
data={[
{label: '100'},
{label: '200'},
{label: '300'},
{label: '400'},
{label: '500'}
]}
components={priceComponents}
/>
</div>
);
};
ChipContainer
Custom ChipContainer component example.import React, {FC} from 'react';
import Tooltip from '@atlaskit/tooltip';
import {
ChipContainerProps,
ChipContainerType,
Components,
components,
TreeMultiSelect
} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomChipContainer: FC<ChipContainerProps> = (props) => (
<Tooltip content={`Tooltip for the ${props.ownProps.label}`}>
<components.ChipContainer {...props}/>
</Tooltip>
);
const ChipContainer: ChipContainerType = {component: CustomChipContainer};
const customComponents: Components = {ChipContainer};
export const CustomChipContainerExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
components={customComponents}
/>
</div>
);
};
ChipLabel
Custom ChipLabel component example.import React, {FC, useMemo} from 'react';
import {ChipLabelProps, Components, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
interface CustomChipLabelProps {
suffix: string;
}
const CustomChipLabel: FC<ChipLabelProps<CustomChipLabelProps>> = (props) => (
<div {...props.attributes}>
{props.ownProps.label}{'-'}{props.customProps.suffix}
</div>
);
export const CustomChipLabelExample: FC = () => {
const data = useMemo(() => getTreeNodeData(true), []);
const components: Components = useMemo(() => (
{
ChipLabel: {
component: CustomChipLabel,
props: {suffix: 'Yo'}
}
}
), []);
return (
<div className="component-example">
<TreeMultiSelect
data={data}
components={components}
/>
</div>
);
};
ChipClear
Custom ChipClear component example.import React, {FC} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faTrash} from '@fortawesome/free-solid-svg-icons';
import {ChipClearProps, ChipClearType, Components, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomChipClear: FC<ChipClearProps> = (props) => (
<div {...props.attributes}>
<FontAwesomeIcon icon={faTrash}/>
</div>
);
const ChipClear: ChipClearType = {component: CustomChipClear};
const components: Components = {ChipClear};
export const CustomChipClearExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
components={components}
/>
</div>
);
};
Input
Custom Input component example.import React, {FC, HTMLProps} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faMagnifyingGlass} from '@fortawesome/free-solid-svg-icons';
import {Components, FieldProps, FieldType, InputProps, InputType, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomField: FC<FieldProps> = (props) => (
<div {...props.attributes}>
<button className="filter-btn">Tree multi select</button>
</div>
);
const CustomFieldInput: FC<InputProps> = (props) => (
<textarea {...props.attributes as unknown as HTMLProps<HTMLTextAreaElement>}/>
);
const CustomDropdownInput: FC<InputProps> = (props) => (
<div style={{display: 'flex', flex: 1, alignItems: 'center'}}>
<input {...props.attributes}/>
<div style={{padding: '0 4px'}}><FontAwesomeIcon icon={faMagnifyingGlass}/></div>
</div>
);
const Field: FieldType = {component: CustomField};
const DropdownInput: InputType = {component: CustomDropdownInput};
const dropdownComponents: Components = {Field, Input: DropdownInput};
const FieldInput: InputType = {component: CustomFieldInput};
const fieldComponents: Components = {Input: FieldInput};
export const CustomInputExample: FC = () => {
return (
<div className="component-example input-example">
<TreeMultiSelect
className="custom-dropdown-input"
data={getTreeNodeData()}
withDropdownInput={true}
components={dropdownComponents}
/>
<TreeMultiSelect
className="custom-field-input"
data={getTreeNodeData()}
components={fieldComponents}
/>
</div>
);
};
FieldClear
Custom FieldClear component example.import React, {FC} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faDeleteLeft} from '@fortawesome/free-solid-svg-icons';
import {Components, FieldClearProps, FieldClearType, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomFieldClear: FC<FieldClearProps> = (props) => (
<div {...props.attributes}>
<FontAwesomeIcon icon={faDeleteLeft}/>
</div>
);
const FieldClear: FieldClearType = {component: CustomFieldClear};
const components: Components = {FieldClear};
export const CustomFieldClearExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
components={components}
/>
</div>
);
};
FieldToggle
Custom FieldToggle component example.import React, {FC} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCaretDown, faCaretUp} from '@fortawesome/free-solid-svg-icons';
import {Components, FieldToggleProps, FieldToggleType, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomFieldToggle: FC<FieldToggleProps> = (props) => (
<div {...props.attributes}>
{props.ownProps.expanded ? <FontAwesomeIcon icon={faCaretUp}/> : <FontAwesomeIcon icon={faCaretDown}/>}
</div>
);
const FieldToggle: FieldToggleType = {component: CustomFieldToggle};
const components: Components = {FieldToggle};
export const CustomFieldToggleExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
components={components}
/>
</div>
);
};
Dropdown
Custom Dropdown component example.import React, {FC} from 'react';
import {Components, DropdownProps, DropdownType, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '@/utils/utils';
const CustomDropdown: FC<DropdownProps> = (props) => {
return (
<div {...props.attributes}>
<div style={{padding: '10px', display: 'flex', justifyContent: 'center', borderBottom: '2px solid #ebebeb'}}>
<label>{'Custom Dropdown top content'}</label>
</div>
{props.children}
<div style={{padding: '10px', display: 'flex', justifyContent: 'center', borderTop: '2px solid #ebebeb'}}>
<label>{'Custom Dropdown bottom content'}</label>
</div>
</div>
);
};
const Dropdown: DropdownType = {component: CustomDropdown};
const components: Components = {Dropdown};
export const CustomDropdownExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true, true)}
components={components}
/>
</div>
);
};
SelectAllContainer
Custom SelectAllContainer component example.import React, {FC} from 'react';
import Tooltip from '@atlaskit/tooltip';
import {Components, SelectAllContainerProps, SelectAllContainerType, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomSelectAllContainer: FC<SelectAllContainerProps> = (props) => (
<Tooltip content={`Tooltip for the ${props.ownProps.label}`}>
<div {...props.aAttributes}>
{props.children}
</div>
</Tooltip>
);
const SelectAllContainer: SelectAllContainerType = {component: CustomSelectAllContainer};
const components: Components = {SelectAllContainer};
export const CustomSelectAllContainerExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
withSelectAll
components={components}
/>
</div>
);
};
SelectAllCheckbox
Custom SelectAllCheckbox component example.import React, {FC} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faSquare, faSquareCheck, faSquareMinus} from '@fortawesome/free-regular-svg-icons';
import {Components, SelectAllCheckboxProps, SelectAllCheckboxType, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomSelectAllCheckbox: FC<SelectAllCheckboxProps> = (props) => (
<div {...props.attributes}>
{props.ownProps.checked
? <FontAwesomeIcon icon={faSquareCheck}/>
: props.ownProps.partial
? <FontAwesomeIcon icon={faSquareMinus}/>
: <FontAwesomeIcon icon={faSquare}/>
}
</div>
);
const SelectAllCheckbox: SelectAllCheckboxType = {component: CustomSelectAllCheckbox};
const components: Components = {SelectAllCheckbox};
export const CustomSelectAllCheckboxExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
withSelectAll
components={components}
/>
</div>
);
};
SelectAllLabel
Custom SelectAllLabel component example.import React, {FC} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCheckDouble} from '@fortawesome/free-solid-svg-icons';
import {Components, SelectAllLabelProps, SelectAllLabelType, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomSelectAllLabel: FC<SelectAllLabelProps> = (props) => (
<div {...props.attributes}>
{props.ownProps.label}{' '}<FontAwesomeIcon icon={faCheckDouble}/>
</div>
);
const SelectAllLabel: SelectAllLabelType = {component: CustomSelectAllLabel};
const components: Components = {SelectAllLabel};
export const CustomSelectAllLabelExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
withSelectAll
components={components}
/>
</div>
);
};
NodeContainer
Custom NodeContainer component example.import React, {FC} from 'react';
import Tooltip from '@atlaskit/tooltip';
import {
Components,
components,
NodeContainerProps,
NodeContainerType,
TreeMultiSelect
} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomNodeContainer: FC<NodeContainerProps> = (props) => (
<Tooltip content={`Tooltip for the ${props.ownProps.label}`}>
<components.NodeContainer {...props}/>
</Tooltip>
);
const NodeContainer: NodeContainerType = {component: CustomNodeContainer};
const customComponents: Components = {NodeContainer};
export const CustomNodeContainerExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
components={customComponents}
/>
</div>
);
};
NodeToggle
Custom NodeToggle component example.import React, {FC} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faMinus, faPlus} from '@fortawesome/free-solid-svg-icons';
import {Components, NodeToggleProps, NodeToggleType, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomNodeToggle: FC<NodeToggleProps> = (props) => (
<div {...props.attributes}>
{props.ownProps.expanded
? <FontAwesomeIcon icon={faMinus}/>
: <FontAwesomeIcon icon={faPlus}/>
}
</div>
);
const NodeToggle: NodeToggleType = {component: CustomNodeToggle};
const components: Components = {NodeToggle};
export const CustomNodeToggleExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
components={components}
/>
</div>
);
};
NodeCheckbox
Custom NodeCheckbox component example.import React, {FC} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faSquare, faSquareCheck, faSquareMinus} from '@fortawesome/free-regular-svg-icons';
import {Components, NodeCheckboxProps, NodeCheckboxType, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomNodeCheckbox: FC<NodeCheckboxProps> = (props) => (
<div {...props.attributes}>
{props.ownProps.checked
? <FontAwesomeIcon icon={faSquareCheck}/>
: props.ownProps.partial
? <FontAwesomeIcon icon={faSquareMinus}/>
: <FontAwesomeIcon icon={faSquare}/>
}
</div>
);
const NodeCheckbox: NodeCheckboxType = {component: CustomNodeCheckbox};
const components: Components = {NodeCheckbox};
export const CustomNodeCheckboxExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
components={components}
/>
</div>
);
};
NodeLabel
Custom NodeLabel component example.import React, {FC} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faHandSpock} from '@fortawesome/free-regular-svg-icons';
import {Components, NodeLabelProps, NodeLabelType, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomNodeLabel: FC<NodeLabelProps> = (props) => (
<div {...props.attributes}>
<FontAwesomeIcon icon={faHandSpock}/>
{props.ownProps.label}
</div>
);
const NodeLabel: NodeLabelType = {component: CustomNodeLabel};
const components: Components = {NodeLabel};
export const CustomNodeLabelExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
components={components}
/>
</div>
);
};
NoData
Custom NoData component example.import React, {FC} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faFaceSadTear} from '@fortawesome/free-regular-svg-icons';
import {Components, NoDataProps, NoDataType, TreeMultiSelect} from 'react-tree-multi-select';
import {getTreeNodeData} from '../../utils';
const CustomNoData: FC<NoDataProps> = (props) => (
<div {...props.attributes}>
<div><FontAwesomeIcon icon={faFaceSadTear}/>{' '}{props.ownProps.label}</div>
</div>
);
const NoData: NoDataType = {component: CustomNoData};
const components: Components = {NoData};
export const CustomNoDataExample: FC = () => {
return (
<div className="component-example">
<TreeMultiSelect
data={getTreeNodeData(true)}
components={components}
/>
</div>
);
};