DEV Community

Cover image for Building A Todo CRUD App With React, TypeScript, Vite And Chakra UI
Ethan
Ethan

Posted on • Originally published at ethan-dev.com

Building A Todo CRUD App With React, TypeScript, Vite And Chakra UI

Introduction

Hello! 😎

Recently I've been learning how to use vite in a couple of projects so I thought I'd take what I've learned and show you how to create a simple Todo CRUD application using the following stack: React, TypeScript, Vite, Chakra UI.

I will also be using JsonServer which is a very useful module if you quickly need a REST server. The requests to the server will be done using the axios module.

Well then let's get started. πŸ˜†


Reasons For Using This Stack

First I want to go over why I'm using the above stack for this tutorial.

  1. React: A popular front-end library. React enables us to craft interactive user interfaces. Its component-based architecture aids in code reusuability and state management. I also deal with more React projects than Vue.js etc.
  2. TypeScript: TypeScript is a superset of JavaScript that adds static types. It helps catch errors early, enhances code quality, and provides better developer experience with IntelliSense.
  3. Vite: Vite is a modern build tool that offers lightning-fast Hot Module Replacement (HMR) for a smoother developer experience. It's optimized for the modern JS ecosystem.
  4. Chakra UI: This is a modular and accessible component library, making it simple to style our React applications without sacrificing functionality or performance.
  5. JsonServer: JsonServer mocks a REST API using a JSON file, letting frontend developers prototype and test without a backend.
  6. Axios: Axios is a JavaScript library that simplifies HTTP requests. It's more intuitive than the built in Fetch API and integrates well with modern async/await syntax.

Now lets start creating the application. 😸


Setting Up The Project

First we need to install the Vite CLI, open up a terminal window and enter the following command:

npm i -g create-vite
Enter fullscreen mode Exit fullscreen mode

Next to create a vite project you need to run the following command:

create-vite vite-todo --template react-ts
Enter fullscreen mode Exit fullscreen mode

The template tells vite we want a React TypeScript application.

Next install the dependencies via the following command:

cd vite-todo && npm i
Enter fullscreen mode Exit fullscreen mode

We also need to install the dependencies that are needed for this project, this can be done via the following command:

npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion
Enter fullscreen mode Exit fullscreen mode

Next we need to set up our mock REST API server, this can be done by creating a simple json file, create a file called "db.json" and populate it with the following:

{
    "todos": []
}
Enter fullscreen mode Exit fullscreen mode

Here we create it with an empty array to indicate there are no todos.

Next start up the mock json server with the following command:

npx json-server --watch db.json --port 5000
Enter fullscreen mode Exit fullscreen mode

The server should now work at the following url:
http://localhost:5000/todos

Accessing it you should get an empty array. Now we can start coding the front end. πŸ™‚


Coding The Frontend

Now we can finally start coding the frontend of the application, open up "src/App.tsx" and replace the contents with the following:

import React, {
  useState,
  useEffect
} from 'react';
import axios from 'axios';
import {
  Box,
  Button,
  Input,
  VStack,
  Checkbox,
  HStack,
  Container,
  Heading,
  Center
} from '@chakra-ui/react';

type Todo = {
  id: number;
  title: string;
  completed: boolean;
};

const App = () => {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [newTodo, setNewTodo] = useState<string>('');

  useEffect(() => {
    const fetchTodos = async (): Promise<void> => {
      const response = await axios.get<Todo[]>('http://localhost:5000/todos');
      setTodos(response.data);
    };

    fetchTodos();
  }, []);

  const addTodo = async (): Promise<void> => {
    const response = await axios.post<Todo>('http://localhost:5000/todos', {
      title: newTodo,
      completed: false
    });

    setTodos([...todos, response.data]);
    setNewTodo('');
  };

  const updateTodo = async (id: number, title: string): Promise<void> => {
    const updatedTodo = { title, completed: false };
    await axios.put(`http://localhost:5000/todos/${id}`);
    setTodos(todos.map(todo => (todo.id === id ? { ...todo, title } : todo)));
  };

  const deleteTodo = async (id: number): Promise<void> => {
    await axios.delete(`http://localhost:5000/todos/${id}`);
    setTodos(todos.filter(todo => todo.id !== id));
  };

  const toggleComplete = async (id: number): Promise<void> => {
    const todo = todos.find(todo => todo.id === id);

    if (todo) {
      const updatedTodo = { ...todo, completed: !todo.completed };
      await axios.put(`http://localhost:5000/todos/${id}`, updatedTodo);
      setTodos(todos.map(todo => (todo.id === id ? updatedTodo : todo)));
    }
  };

  return (
    <Container maxW="container.md" p={5}>
      <Center>
        <Heading mb={5}>Simple Todo App</Heading>
      </Center>

      <Input
        value={ newTodo }
        onChange={
          (e) => setNewTodo(e.target.value)
        }
        placeholder="Add a new todo"
      />

      <Center mt={2} mb={5}>
        <Button
          onClick={ addTodo }
          mt={2}
        >Add Todo</Button>
      </Center>

      <VStack spacing={ 4 }>
        { todos.map(todo => (
          <HStack key={ todo.id }>
            <Checkbox
              isChecked={ todo.completed }
              onChange={
                () => toggleComplete(todo.id)
              }
            />
            <Input
              defaultValue={ todo.title }
              onBlur={
                (e) => updateTodo(todo.id, e.target.value)
              }
            />
            <Button onClick={
              () => deleteTodo(todo.id)
            }>
              Delete
            </Button>
          </HStack>
        ))}
      </VStack>
    </Container>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

With the above code we import the needed dependencies, define a Todo type and create a basic UI using Chakra UI. We also provide funtions for each CRUD operation.

Next we need to edit the "src/main.tsx" file in order to display our application, open the file up and replace the contents with the following:

import React from 'react';
import { createRoot } from 'react-dom/client';
import { ChakraProvider } from '@chakra-ui/react';
import App from './App';

const container = document.getElementById('root');
const root = createRoot(container);

root.render(
  <React.StrictMode>
    <ChakraProvider>
      <App />
    </ChakraProvider>
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

All done! πŸ˜ƒ In order to run the application run the following command:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Next open up your browser and access the following URL:
http://localhost:5173/

You should now see the following page: (I've added a couple of todos)

Image of app

Done! 😺 Feel free to play around with the UI etc.


Conclusion

In this tutorial I have shown you how to create a base Todo application using React, Typescript, Vite and Chakra UI, using JsonServer as the REST API and using axios to make requests to the server.

I hope this tutorial has helped you get started and I had a lot of fun learning about Vite.

Feel free to try replacing the mock REST API with a real API and database and maybe try and improve the UI.

As usual you can find the code for the tutorial on my Github:
https://github.com/ethand91/vite-todo

Happy Coding! 😎


If you appreciate my work? I cover a variety of topics. For more, please like and follow me.
Also, I love coffee.

β€œBuy Me A Coffee”

If you are looking to learn Algorithm Patterns to ace the coding interview I recommend the following course

Top comments (0)