Ant Design is a React UI library that has a plethora of easy-to-use components that are useful for building elegant user interfaces.
Created by Chinese conglomerate Alibaba, Ant Design is used by several big names: Alibaba (of course), Tencent, Baidu, and more. While Material-UI remains the most popular React UI library with over 40k stars on Github, Ant Design is currently at a close second, and is quickly closing-in the gap.
There’s also a mobile version of Ant Design, and you can learn more about it here.
In this tutorial, we’ll build a basic todo application to showcase a few of Ant Design’s components. Our first step will be to set up our boilerplate. I’ve done so using create-react-app.
Then we’ll need to add the antd
dependency to the project:
$ yarn add antd
# or, using npm:
$ npm install antd --save
Before we start building our <Todo />
component, we’ll add a reference to it in the root component:
import React from "react";
import ReactDOM from "react-dom";
import Todo from "./todo";
import "./styles.css";
function App() {
return (
<div className="App">
<Todo />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Now we can start building our <Todo />
component. Open a new file called todo.js
with the following contents:
import React from "react";
export default class Todo extends React.Component {
render() {
return (
<div className="todoContainer">
<h1>TODO App</h1>
</div>
);
}
}
I’m going to edit the stylesheet so that the .todoContainer
is nicely centered on the page.
.App {
font-family: sans-serif;
text-align: center;
}
.todoContainer {
width: 75%;
margin-left: auto;
margin-right: auto;
}
Cool! Now let’s add an input to the <Todo />
component. We’ll use the one provided by antd
, making sure to also import the CSS file for the library.
import React from "react";
import { Input } from "antd";
// Don't forget to include the CSS styles for antd!
import "antd/dist/antd.css";
export default class Todo extends React.Component {
render() {
return (
<div className="todoContainer" />
<h1>TODO App</h1>
<Input
placeholder="What needs to be done?"
/>
</div>
);
}
}
Great, now we are rendering a text input box. However, it doesn’t do anything. We need our input to add its content to the component’s state so we can render a list of todos. We’ll use the onPressEnter
prop on our <Input />
to detect when the user submits a new todo:
// --- snip ---
export default class Todo extends React.Component {
constructor() {
super();
// Initialize the state
this.state = {
todos: []
};
}
handlePressEnter = e => {
// Create a todo object containing its index and content
const todo = {
index: this.state.todos.length,
content: e.target.value
};
// Add the todo to our array
const newTodos = this.state.todos.concat(todo);
this.setState({
todos: newTodos
});
// Clear input
e.target.value = "";
};
render() {
return (
<div className="todoContainer">
<h1>TODO App</h1>
<Input
placeholder="What needs to be done?"
onPressEnter={this.handlePressEnter}
/>
</div>
);
}
}
Now handlePressEnter()
will update the component’s state whenever the user inputs a new todo item, but we still have to render them.
For this purpose we can use Ant Design’s <List />
component. It takes in an array as its dataSource
prop and renders it according to whatever function is passed to its renderItem
prop.
import React from "react";
import { Input, List } from "antd";
import "antd/dist/antd.css";
export default class Todo extends React.Component {
// --- snip ---
render() {
return (
<div className="todoContainer">
<h1>TODO App</h1>
<Input
placeholder="What needs to be done?"
onPressEnter={this.handlePressEnter}
/>
<List
{/* emptyText sets the text to display in an empty list */}
locale={{ emptyText: "No todo items" }}
dataSource={this.state.todos}
renderItem={item => (
<List.Item>{item.content}</List.Item>
)}
/>
</div>
);
}
}
Ta-da! Now we have our basic todo app working! However, there’s still more that we can do to make it better!
Our todo application is up and running. However, there is more that we need to add: most importantly, a remove button. We’ll also add a date picker, because why not?
To avoid making our <Todo />
overly complicated, I’m going to extract the <List.Item />
into its own separate component. I will also define a new method, removeTodo()
to remove todo items given its index. I’ll then pass that method into our new component.
To serve as a remove button, we can use Ant’s <Icon />
component. Add it to the <List.Item />
's action
prop, which takes an array of components. To see the full list of icons, see this page on the Ant Design website.
import React from "react";
import { Input, List, Icon } from "antd";
import "antd/dist/antd.css";
export default class Todo extends React.Component {
// --- snip ---
removeTodo = index => {
let newTodos = [...this.state.todos];
// Remove element
newTodos.splice(index, 1);
// Decrement greater indexes
for (let i = index; i < newTodos.length; i++) {
newTodos[i].index -= 1;
}
// Update state
this.setState({
todos: newTodos
});
};
render() {
return (
<div className="todoContainer">
<h1>TODO App</h1>
<Input
placeholder="What needs to be done?"
onPressEnter={this.handlePressEnter}
/>
<List
locale={{ emptyText: "No todo items" }}
dataSource={this.state.todos}
renderItem={item => (
<TodoItem
todo={item}
removeTodo={this.removeTodo}
/>
)}
/>
</div>
);
}
}
class TodoItem extends React.Component {
remove = () => {
// Remove this TodoItem
this.props.removeTodo(this.props.todo.index);
};
render() {
return (
<List.Item
actions={[
<Icon
type="close-circle"
theme="filled"
onClick={this.remove}
/>
]}
>
{this.props.todo.content}
</List.Item>
);
}
}
Great! Now all that’s left is to add Ant’s <DatePicker />
component. To do so, we’re going to update the handlePressEnter()
method by adding date
and dateString
properties to the todo objects we’re storing in our state. Then we’ll need an additional method, setDate()
, to update those properties.
import React from "react";
import { Input, List, Icon, DatePicker } from "antd";
import "antd/dist/antd.css";
export default class Todo extends React.Component {
// --- snip ---
handlePressEnter = e => {
// Create a todo object containing its index, content,
// as well as an empty date
const todo = {
index: this.state.todos.length,
content: e.target.value,
date: null,
dateString: ""
};
// Add the new todo to our array
const newTodos = this.state.todos.concat(todo);
this.setState({
todos: newTodos
});
// Clear input
e.target.value = "";
};
setDate = (index, date, dateString) => {
// Set the date of the given todo
let newTodos = [...this.state.todos];
newTodos[index].date = date;
newTodos[index].dateString = dateString;
// Initialize the state
this.setState({
todos: newTodos
});
};
render() {
return (
<div className="todoContainer">
<h1>TODO App</h1>
<Input
placeholder="What needs to be done?"
onPressEnter={this.handlePressEnter}
/>
<List
locale={{ emptyText: "No todo items" }}
dataSource={this.state.todos}
renderItem={item => (
<TodoItem
todo={item}
removeTodo={this.removeTodo}
setDate={this.setDate}
/>
)}
/>
</div>
);
}
}
class TodoItem extends React.Component {
remove = () => {
// Remove this TodoItem
this.props.removeTodo(this.props.todo.index);
};
handleDateChange = (date, dateString) => {
// Update the date when changed
this.props.setDate(this.props.todo.index, date, dateString);
}
render() {
return (
<List.Item
actions={[
<DatePicker
format="MM/DD/YYYY"
onChange={this.handleDateChange}
value={this.props.todo.date}
/>,
<Icon
type="close-circle"
theme="filled"
onClick={this.remove}
/>
]}
>
{this.props.todo.content}
</List.Item>
);
}
}
And voilà! 🎉
We now have our fully functional todo app utilizing Ant Design! If you’d like to learn more about Ant, check out their docs.
You can find the full code for this post on CodeSandbox.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!