How to make a Chakra UI and React to-do list app using hooks only

There are many ways to make your application look beautiful on screen, you can use vanilla CSS or you can choose from a variety of available CSS frameworks. Today, I have built a simple to-do-list app in React using Chakra UI to make the design attractive, yet simple. Chakra UI is very similar to material UI, so if you have already worked with one of them, the other will be easy to understand.

You can visit this link to go through the complete documentation of Chakra UI.

Set up

Let’s start with the coding. Please follow the following steps:

  • Create a new react project using:
npx create-react-app todo_list
  • Do the necessary clean-up (delete the tests.js file, logo.svg, and unnecessary code from App.js)
  • Install chakra-ui by typing:
npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4 
  • You can refer to the yarn command for installation from the docs if you are using yarn instead of node

Code

index.js

Now, we will do the Chakra set up in our app. For this, go to the root of your app, i.e., the index.js file, and write the following code:

import App from './App';
import { ChakraProvider } from "@chakra-ui/react"

ReactDOM.render(
  <React.StrictMode>
     <ChakraProvider>
         <App />
         </ChakraProvider>
  
  </React.StrictMode>,
  document.getElementById('root')
);

App.js

Now, we can start working with Chakra UI in our app.

First, we’ll create a stacking component called VStack in the App.js file to vertically assemble the elements on the page. We will also create the AddTodo and TodoList components separately to make the app look cleaner. The App.js file should look like this:

import {  VStack,Text } from "@chakra-ui/react"
import './App.css';
import {useState} from 'react'
import AddTodo from "./components/AddTodo";
import TodoList from "./components/TodoList";

function App() {
 
const todosList = [
  { id: 1, text: 'Buy eggs'},
  { id: 2, text: 'Walk the dog'},
  { id:3, text: 'Watch a movie'}
];

const [todos, setTodos] = useState(todosList);

  return (
    <VStack p={5}>
    
    <Text bgGradient="linear(to-l, #7928CA,#FF0080)"
      bgClip="text"
      fontSize="6xl"
      fontWeight="extrabold">
      Todo App
    </Text>
       
    <TodoList todos={todos} deleteTodo={deleteTodo} editTodo={editTodo}/>
    <AddTodo addTodo={addTodo}/>
    </VStack>
  );
}
export default App;

TodoList.js

Now, let’s move to the TodoList.js file, where we have displayed the existing todos and newly created ones. I will be using the following items from Chakra-UI on this page:

  • VStack: To align all the list items vertically
  • HStack: To horizontally space the list item
  • Flex: To put some space between the text of the list item and the delete/edit icons
  • Badge: To display a message if no todos exist
import { HStack, VStack,Text, Flex, Badge, Button, Input, Divider } from '@chakra-ui/react'
import { DeleteIcon, EditIcon} from '@chakra-ui/icons'
import React,{useState} from 'react'


function TodoList({ todos, deleteTodo, editTodo }) {

    return (
       !todos.length ? 
       <Badge 
       colorScheme="purple" 
       variant="outline"
       borderRadius="4"
       p='4' m='5'
       >No todos for Today!!</Badge> : (
        <VStack>
        {todos.map((todo) => ( 
            <HStack spacing="24px" w="320px">
                <Flex p={6} w="300px" h="50px" justifyContent="space-between">
                <Text>{todo.text}</Text>

                <Flex w="10px" >
                
                <DeleteIcon color="red.500" mr="2" onClick={()=>deleteTodo(todo.id)}/>
                <EditIcon onClick={()=>handleEditClick(todo)} />
                 
                </Flex>
                
            
        </Flex> 
            </HStack>  
            ))} 
        </VStack>
        )   ) } 
export default TodoList

AddTodo.js

Now, let’s move to the AddTodo.js component, which makes use of the following features from Chakra-UI:

  • Stack: To stack the input field and Add Todo button
  • Input: To style our input element
  • Button: To get a nice Add Todo button
  • Toast: To display a message if the todo content is empty
import { Stack, Input,Button,useToast } from '@chakra-ui/react'
import React, {useState} from 'react'
import { nanoid } from 'nanoid';


function AddTodo({ addTodo }) {
const toast = useToast()
const [value, setValue] = useState("")

function handleSubmit(e){
    e.preventDefault();

if(value === ''){
    toast({
        title: "Please enter the text.",
        status: "warning",
        duration: 2000,
        isClosable: true,
      })
      return;
    }
const todo = {
    id: nanoid(),
    text: value
}

addTodo(todo)
setValue('')

}
    return (
        <form onSubmit={handleSubmit}>
        <Stack spacing={5}>
            <Input
            mt={5} 
            value={value} 
            variant="outline" 
            type="text" 
            placeholder="Enter your todo..."
            onChange={(e)=>setValue(e.target.value)} />
            <Button colorScheme="teal" type="submit">Add Todo</Button>
        </Stack>
        </form>
        
    )
}

export default AddTodo

Updated TodoList.js

For the Edit functionality, I have used the modal from Chakra-UI where a user can update the value of todo. The updated code of the TodoList.js, including the edit functionality, is as follows: (I have added comments in the code to better explain the react hooks functionality.)

import { HStack, VStack,Text, Flex, Badge,Modal,ModalOverlay,ModalContent,ModalHeader,ModalFooter,ModalBody,
    ModalCloseButton, Button, Input, Divider } from '@chakra-ui/react'
import { DeleteIcon, EditIcon} from '@chakra-ui/icons'
import React,{useState} from 'react'


function TodoList({ todos, deleteTodo, editTodo }) {
const [todo, setTodo] = useState(""); 
//set the todo value in the modal:
const [modalValue, setModalValue] = useState({})
//hook to close the modal when user is done editing:
const [isOpen,setIsOpen] = useState(false)   

function onClose(){
    setIsOpen(false)
  }

function handleEditClick(todo){
   setIsOpen(true)
// we've set the passed todo to modal value
   setModalValue(todo)
   console.log(todo)
}

function handleEditInputChange(e,id){ 
setModalValue({ ...modalValue, text: e.target.value });
}

function handleEditSubmit(e){
  e.preventDefault();
  editTodo(modalValue.id,modalValue)
  setModalValue("")
  setIsOpen(false)
}

    return (

       !todos.length ? 
       <Badge 
       colorScheme="purple" 
       variant="outline"
       borderRadius="4"
       p='4' m='5'
       >No todos for Today!!</Badge> 
       : (
        <VStack>
        {todos.map((todo) => (
            
            <HStack spacing="24px" w="320px">
                <Flex p={6} w="300px" h="50px" justifyContent="space-between">
                <Text>{todo.text}</Text>
          

                <Flex w="10px" >
                
                <DeleteIcon color="red.500" mr="2" onClick={()=>deleteTodo(todo.id)}/>
                <EditIcon onClick={()=>handleEditClick(todo)} />    
            </Flex>
                
            {/* modal for editing a todo */}
            <Modal isOpen={isOpen} onClose={onClose}>
            <ModalOverlay />
            <ModalContent>
            <ModalHeader>Edit Your Todo</ModalHeader>
            <ModalCloseButton />
            <form onSubmit={handleEditSubmit}>
            <ModalBody>
            <Input   
            value={modalValue.text} 
            key={modalValue.id}
            variant="outline" 
            type="text" 
            placeholder="Update your todo..."
            onChange={handleEditInputChange} />
            </ModalBody>
            <ModalFooter>
            <Button colorScheme="teal" mr={3} onClick={onClose}>
            Close
            </Button>
            <Button type="submit" colorScheme="teal" mr={3}>
            Update
            </Button>
            </ModalFooter>
          </form>
          
          </ModalContent>
          </Modal>
        </Flex> 
            </HStack>   
            ))} 
        </VStack>
        ) 
        )  }   
export default TodoList

Delete functionality in App.js

The delete functionality can also be found in the final version of App.js file. Take a look at the function below:

function deleteTodo(id){
const newTodos = todos.filter((item)=> {
  return item.id !== id 
})
setTodos(newTodos)
console.log(newTodos)
}

You can find the complete source code here, and the final deployed app here.

Free Resources