Tutorial

Crafting Beautiful UIs in React Using Ant Design

Published on September 7, 2018
author

Alex Taylor

Crafting Beautiful UIs in React Using Ant Design

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.

Getting Started

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:

index.js
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);

Building the Todo Component

Now we can start building our <Todo /> component. Open a new file called todo.js with the following contents:

todo.js
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.

styles.css
.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.

todo.js
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:

todo.js
// --- 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.

todo.js
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!

Getting Fancy

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.

todo.js
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.

todo.js
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.

Learn more about our products

About the authors
Default avatar
Alex Taylor

author

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.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


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!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.