Building a React Todo List with TypeScript and Tailwind CSS

Introduction

Welcome to this tutorial where we’ll walk through building a robust Todo List application using React with TypeScript and styling it beautifully with Tailwind CSS. This combination of technologies is not just powerful but also modern, making it an excellent stack for both new and experienced developers.

In this hands-on project, you will learn to use TypeScript to enhance your React components with type safety, ensuring that your application is robust and less prone to errors. We will also leverage Tailwind CSS for styling, allowing us to create responsive and aesthetically pleasing designs without needing to delve deeply into custom CSS.

By the end of this tutorial, you will not only have a functional Todo List app but also a deeper understanding of how to integrate these technologies seamlessly. Let’s get started!

Setting Up the Project

To begin with, you’ll need to set up your development environment. We will use Create React App to bootstrap our application as it comes with a TypeScript template. Open your terminal and run the following command:

npx create-react-app react-todo-list --template typescript

Once your project is created, navigate into your project directory:

cd react-todo-list

Next, we’ll install Tailwind CSS by following the setup instructions from the official documentation. This involves installing the library and its dependencies:

npm install -D tailwindcss postcss autoprefixer

After installing the necessary packages, initialize the Tailwind configuration by running:

npx tailwindcss init -p

This command will create two configuration files, tailwind.config.js and postcss.config.js. Open tailwind.config.js to configure the paths to your template files, ensuring that Tailwind can purge unused styles in production:

module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [],
}

Now, let’s include the Tailwind directives in our CSS. Go to src/index.css and replace its contents with the following:

@tailwind base;
@tailwind components;
@tailwind utilities;

Your setup is now complete! You can run your application using npm start to see the default React application in your browser. Now that we have everything ready, let’s move on to implementing the code for our Todo List.

Creating the Todo List Component

We’ll start by creating a new component for our Todo List. Inside the src directory, create a new folder named components and a file called TodoList.tsx. In this file, we will define the structure and state of our Todo List.

Here’s a basic outline of what our state will look like:

import React, { useState } from 'react';

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

const TodoList: React.FC = () => {
  const [todos, setTodos] = useState([]);

  return (
    

Todo List

    {todos.map((todo) => (
  • {todo.text}
  • ))}
); }; export default TodoList;

In this code snippet, we define a Todo interface to type our Todo items. We use the useState hook to manage our Todo list’s state, starting with an empty array. The todos state will hold our Todo items, which we’ll display in an unordered list.

Next, we need to import the TodoList component into our main application file, src/App.tsx, to render it. Here’s how you can do that:

import React from 'react';
import TodoList from './components/TodoList';
import './index.css';

const App: React.FC = () => {
  return (
    
); }; export default App;

Now, if you check your application in the browser, you should see the title ‘Todo List’ on the screen, along with an empty list underneath.

Adding Todo Items

Next, we need a way to add new Todo items to our list. We will create a form that will take user input and add it to our todos state when submitted.

Let’s enhance our TodoList.tsx component by adding an input field and a submit button. We will also manage the input state separately:

const TodoList: React.FC = () => {
  const [todos, setTodos] = useState([]);
  const [inputValue, setInputValue] = useState('');

  const addTodo = (e: React.FormEvent) => {
    e.preventDefault();
    if (!inputValue.trim()) return;

    const newTodo: Todo = {
      id: Date.now(),
      text: inputValue,
      completed: false,
    };

    setTodos([...todos, newTodo]);
    setInputValue('');
  };

  return (
    

Todo List

setInputValue(e.target.value)} className="border p-2 rounded mr-2" placeholder="Add a new todo..." />
    {todos.map((todo) => (
  • {todo.text}
  • ))}
); };

In this snippet, we introduced a new state variable inputValue to keep track of the current input. The addTodo function is invoked when the form is submitted, which creates a new Todo item and updates the todos state accordingly. The input field resets after adding a todo.

Now when you run the application, you should be able to add new Todo items to your list!

Marking Todos as Completed

Next, let’s implement functionality that allows users to mark a Todo item as completed. This is a great opportunity to explore state management further and see how we can update specific items within our array of Todos.

We’ll modify our TodoList component to include a checkbox for each Todo item. When the checkbox is clicked, we will toggle the completed status of that specific Todo:

const toggleTodo = (id: number) => {
  setTodos(todos.map(todo => 
    todo.id === id ? { ...todo, completed: !todo.completed } : todo
  ));
};

return (
  
    {todos.map((todo) => (
  • ))}
);

Here, we have a new function toggleTodo that updates the completed status of a Todo when its checkbox is clicked. We use the map function to return a new array of todos, with the completed state toggled for the matched id.

With this implementation, you can click on the checkboxes next to your Todo items to mark them as complete, making your app interactive and functional!

Styling with Tailwind CSS

Now that our Todo List application is functional, it’s time to enhance its look and feel with Tailwind CSS. Tailwind allows you to create a responsive design with utility classes. Let’s add some styling to our components.

We already used some Tailwind classes in our previous snippets, but let’s organize our styling further. Here’s how we can enhance the styling of our form and list, and add transitions for a smoother look:

return (
  

Todo List

setInputValue(e.target.value)} className="border rounded-lg p-2 flex-grow mr-2 shadow-md focus:outline-none focus:ring-2 focus:ring-blue-400" placeholder="Add a new todo..." />
    {todos.map((todo) => (
  • ))}
);

With these enhancements, our Todo List application looks polished and modern. The transition effects provide a dynamic feel when marking items as completed or when hovering over buttons.

Conclusion

Congratulations! You have successfully built a Todo List application using React, TypeScript, and Tailwind CSS. Not only did you learn how to manage state and user input in React, but you also discovered how to leverage TypeScript for type safety and Tailwind for efficient styling.

This project serves as a great foundation from which you can explore more complex applications, such as managing more advanced state with context API or integrating backend services for persistent data storage.

Remember that the key to mastering web development lies in continuous practice and exploration. Feel free to extend this Todo List application by adding features such as filtering tasks, editing existing Todos, or even persisting the Todos in local storage. Keep pushing your boundaries, and happy coding!

Scroll to Top