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

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

create-vite vite-todo --template react-ts

The template tells vite we want a React TypeScript application.

Next install the dependencies via the following command:

cd vite-todo && npm i

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

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": []
}

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

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;

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>
);

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

npm run dev

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