Learn how to create a tooltip in react.
Tooltip is used to shows a dialog with info text when hovered over an element.
We are going to similar tooltip component as you can see in the above picture which will have different placement options.
For development we will need few extra packages.
prop-types
: To validate the props.classnames
: With this we can use CSS classes as javascript object, we just need to name our CSS file asfilename.module.css
.react-spring
: Helps to add animation to the component while mounting and unmouting.
There are two ways in which we can create this component.
1. By creating a functional component which will generate the tooltip but the it style and toggle state will be controlled by CSS.
2. We create a stateful component and render the tooltip when it is active state. We have to maintain the state over here.
The second approach is much better because you can complete control the component, for example you can decide if the tooltip should be visible initially or not.
We are also going to create our component with the second approach.
Let us start the development by importing all the packages and setting the structure of the component.
import React, { Component } from "react"; import styles from "./index.module.css"; import PropTypes from "prop-types"; import cx from "classnames"; import { Spring } from "react-spring/renderprops"; class ToolTip extends Component { //Other code will go here. } export default ToolTip;
Validate the props, so that we can get good idea about how we should layout the elements.
static propTypes = { text: PropTypes.string.isRequired, placement: PropTypes.oneOf(["top", "bottom", "left", "right"]).isRequired, active: PropTypes.bool.isRequired, classname: PropTypes.string, children: PropTypes.node }; static defaultProps = { text: "", placement: "bottom", active: false };
Also set the default props if the required props are missing.
Now create the state to track the changes within the component.
state = { active: this.props.active }; show = () => { this.setState({ active: true }); }; hide = () => { this.setState({ active: false }); };
This will not be controlled by the parent completely, it will maintain its own state to toggle the tooltip text. Props will be useful for initial rendering only.
Layout of tooltip.
As tooltip text are shown next to the parent, we will get the children and place it in a wrapper and create the tooltip text next to it.
render() { const { text, placement, children, classname } = this.props; const { active } = this.state; return ( <div className={cx(styles.tooltip, classname)} onMouseEnter={this.show} onMouseLeave={this.hide} > {children} {active && ( <Spring from={{ opacity: 0 }} to={{ opacity: 1 }}> {props => ( <span className={cx(styles.tooltiptext, styles[placement])} style={props} > {text} </span> )} </Spring> )} </div> ); }
As we can use the CSS classes as objects, styles[placement]
dynamically adds the respective class.
React does not have hover
event listener, instead we are using onMouseEnter
and onMouseLeave
which are combined to use as hover
.
onMouseEnter
the active
state changes to true
and tooltip text is rendered and onMouseLeave
vice versa happens.
Spring
will animate the component while mounting and unmounting.
Complete code of tooltip component in react
import React, { Component } from "react"; import styles from "./index.module.css"; import PropTypes from "prop-types"; import cx from "classnames"; import { Spring } from "react-spring/renderprops"; class ToolTip extends Component { static propTypes = { text: PropTypes.string.isRequired, placement: PropTypes.oneOf(["top", "bottom", "left", "right"]).isRequired, active: PropTypes.bool.isRequired, classname: PropTypes.string, children: PropTypes.node }; static defaultProps = { text: "", placement: "bottom", active: false }; state = { active: this.props.active }; show = () => { this.setState({ active: true }); }; hide = () => { this.setState({ active: false }); }; render() { const { text, placement, children, classname } = this.props; const { active } = this.state; return ( <div className={cx(styles.tooltip, classname)} onMouseEnter={this.show} onMouseLeave={this.hide} > {children} {active && ( <Spring from={{ opacity: 0 }} to={{ opacity: 1 }}> {props => ( <span className={cx(styles.tooltiptext, styles[placement])} style={props} > {text} </span> )} </Spring> )} </div> ); } } export default ToolTip;
Complete style code of tooltip component
The style code are specific for a tooltip.
We have created a wrapper with relative position and then placed the tooltip text as an absolute child inside it.
Then we have provided different classes for the placement and the arrow direction.
import React, { Component } from "react"; import styles from "./index.module.css"; import PropTypes from "prop-types"; import cx from "classnames"; import { Spring } from "react-spring/renderprops"; class ToolTip extends Component { static propTypes = { text: PropTypes.string.isRequired, placement: PropTypes.oneOf(["top", "bottom", "left", "right"]).isRequired, active: PropTypes.bool.isRequired, classname: PropTypes.string, children: PropTypes.node }; static defaultProps = { text: "", placement: "bottom", active: false }; state = { active: this.props.active }; show = () => { this.setState({ active: true }); }; hide = () => { this.setState({ active: false }); }; render() { const { text, placement, children, classname } = this.props; const { active } = this.state; return ( <div className={cx(styles.tooltip, classname)} onMouseEnter={this.show} onMouseLeave={this.hide} > {children} {active && ( <Spring from={{ opacity: 0 }} to={{ opacity: 1 }}> {props => ( <span className={cx(styles.tooltiptext, styles[placement])} style={props} > {text} </span> )} </Spring> )} </div> ); } } export default ToolTip;
Input
import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; import ToolTip from "./Components/Tooltip"; import * as serviceWorker from "./serviceWorker"; ReactDOM.render( <div className="abc"> <ToolTip text="Left" placement={"left"} active={true}> <div style={{ padding: "5px", border: "2px solid", borderRadius: "4px" }}> ToolTip Left </div> </ToolTip> <ToolTip text="Right" placement={"right"} active={true}> <div style={{ padding: "5px", border: "2px solid", borderRadius: "4px" }}> ToolTip Right </div> </ToolTip> <ToolTip text="Top" placement={"top"} active={true}> <div style={{ padding: "5px", border: "2px solid", borderRadius: "4px" }}> ToolTip Top </div> </ToolTip> <ToolTip text="Bottom" placement={"bottom"} active={true}> <div style={{ padding: "5px", border: "2px solid", borderRadius: "4px" }}> ToolTip Bottom </div> </ToolTip> </div>, document.getElementById("root") ); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister();