DEV Community

NasreenKhalid
NasreenKhalid

Posted on • Edited on

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 todo-list app in React using Chakra UI to make the design attractive yet simple.
Chakra UI is very much similar to material UI, so if you have already worked with one of them, the other will be much easier for you to grab on.
You can visit this link to go through the complete documentation of chakra UI.

Let's start with the coding now and 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
  • Now we will do the Chakra setup in our app, for this go to the root of your app i-e 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')
);
Enter fullscreen mode Exit fullscreen mode

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 assemble the elements vertically on the page. Also, we will create the AddTodo and TodoList components separately to make the app look cleaner.
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;
Enter fullscreen mode Exit fullscreen mode

Now, let's move to the TodoList.js file where we have displayed the existing todos and newly created ones also. 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
Enter fullscreen mode Exit fullscreen mode

Now, let's move to 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

AddTodo.js file:

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
Enter fullscreen mode Exit fullscreen mode

For the Edit functionality, I have used the modal from Chakra-ui where user can update the value of todo, the updated code of TodoList.js including the edit functionalty is as follows: (I have addded 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
Enter fullscreen mode Exit fullscreen mode

The delete functionality can also be found in the final version of App.js file. It is the below function that does the removal:

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

You can find the complete source code here
And the final deployed app here

If you are looking for a learning experience that makes you a professional developer with hands-on coding skills, join one of the best courses here

That's all folks..
Happing coding...

Top comments (0)