AlgoInsight

Q&A

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

By AlgoInsight Team2026-06-07arraysdata-structuresfrontendreactreact-hooksweb-developmentcssjavascript

Overview

This combines two ideas: hooks-only React (no class components - all state via useState) and Chakra UI (an accessible, themeable component library so you write almost no raw CSS). The to-do list is a canonical CRUD-in-state exercise: a list of task objects you add to, toggle complete, and remove - always treating state immutably.

graph TD
    A["useState: tasks[], input"] --> B[Add: setTasks([...tasks, new])]
    A --> C[Toggle: map → flip done flag]
    A --> D[Delete: filter out by id]
    B & C & D --> E[Chakra components re-render list]
    style A fill:#1e1b4b,color:#fff

Setup

npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion

Wrap your app in Chakra's provider:

import { ChakraProvider } from "@chakra-ui/react";
export default function Root() {
  return <ChakraProvider><TodoApp /></ChakraProvider>;
}

The Component (hooks only)

import { useState } from "react";
import {
  Box, Heading, Input, Button, Stack, HStack,
  Checkbox, IconButton, Text
} from "@chakra-ui/react";
import { DeleteIcon } from "@chakra-ui/icons";

export default function TodoApp() {
  const [tasks, setTasks] = useState([]);   // [{ id, text, done }]
  const [input, setInput] = useState("");

  const addTask = () => {
    const text = input.trim();
    if (!text) return;
    setTasks(prev => [...prev, { id: Date.now(), text, done: false }]);
    setInput("");                            // clear the field
  };

  const toggleTask = (id) =>
    setTasks(prev => prev.map(t => t.id === id ? { ...t, done: !t.done } : t));

  const deleteTask = (id) =>
    setTasks(prev => prev.filter(t => t.id !== id));

  return (
    <Box maxW="480px" mx="auto" mt={10} p={6} borderWidth="1px" borderRadius="lg">
      <Heading size="md" mb={4}>My Tasks</Heading>

      <HStack mb={4}>
        <Input
          value={input}
          placeholder="Add a task…"
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && addTask()}
        />
        <Button colorScheme="purple" onClick={addTask}>Add</Button>
      </HStack>

      <Stack spacing={2}>
        {tasks.length === 0 && <Text color="gray.500">No tasks yet.</Text>}
        {tasks.map(task => (
          <HStack key={task.id} justify="space-between">
            <Checkbox isChecked={task.done} onChange={() => toggleTask(task.id)}>
              <Text as={task.done ? "s" : undefined}>{task.text}</Text>
            </Checkbox>
            <IconButton
              aria-label="Delete task"
              icon={<DeleteIcon />}
              size="sm"
              onClick={() => deleteTask(task.id)}
            />
          </HStack>
        ))}
      </Stack>
    </Box>
  );
}

What the Hooks Are Doing

What Chakra Provides

Box, Stack, HStack handle layout/spacing via props (mb, spacing, justify); Input, Button, Checkbox, IconButton are accessible, themed components. You set colors with colorScheme and never touch a CSS file. Chakra components are keyboard- and screen-reader-friendly out of the box.

Optional Enhancements

Why This Matters in Interviews

This exercises hooks-only state management, controlled inputs, immutable updates (spread/map/filter), list rendering with stable keys, and integrating a component library. The discriminators: using functional state updates, never mutating the array in place, and giving each list item a stable key. It also shows you can build accessible UI quickly with a design system.

Common Pitfalls

References

More answers