Create a Todo App with NextJS

Preview

Hi, there.
Today I am going to show you how to build a little todo app using NextJS and SASS. If you have any Feedback just leave it down in the comments.

You can try it on your own: Live Demo

Link to Code


This content originally appeared on DEV Community and was authored by Fabian Bandini

Preview

Hi, there.
Today I am going to show you how to build a little todo app using NextJS and SASS. If you have any Feedback just leave it down in the comments.

You can try it on your own: Live Demo

Link to Code


Initialize the project

mkdir todo-app
cd todo-app
npm init -y
npm install sass next react react-dom

Change package.json

{
  "name": "todo-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev":"next dev"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "next": "^0.0.3",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "sass": "^1.53.0"
  }
}

Finally type npm run dev in the console and go to localhost in your browser.

Create the filestructure

Filestrucutr efor the project

Creating the logic for our todo app

styles/global.scss
* {
  font-family: sans-serif;
  padding: 0;
  margin: 0;
  background: #1A1B1E;
}
pages/_app.js
import "../styles/global.scss";

export default function App({Component, pageProps}){
    return (
        <Component {...pageProps} />
    );
}
components/TodoList.js
export default function TodoList(){
    return (
        <>

        </>
    );
}

This Component will handle the input of the user and creating new todos.

For now we will just create the components. After the logic is implemented we wil add some styling.

components/TodoItem.js
import {useState} from "react";

export default function TodoItem({title, id, handleClickRemove}){
    const [done, setIsDone] = useState(false);

    const markDone = () => {
        setIsDone(!done);
    }

    return (
        <div onClick={markDone}>
            <h2>{title}</h2>
            <button onClick={() => handleClickRemove(id)}></button>
        </div>
    );
}

The above seen component will take care modelling our todos. Back in our TodoList.js we will create the logic for adding a todo and displaying them accordingly.

components/TodoList.js
import {useState} from "react";
import TodoItem from "./TodoItem";

export default function TodoList() {
    const [todos, setTodos] = useState([]);
    const [currentInput, setCurrentInput] = useState("");

    const handleClickAdd = (e) => {
        e.preventDefault();

        if (currentInput === "") {
            //input validation
            return;
        }

        const newTodo = {
            id: currentInput + Math.random().toString(),
            content: currentInput
        }

        setTodos([newTodo, ...todos]);
        setCurrentInput("")
    }

    const handleClickRemove = (id) => {
        const filteredTodos = todos.filter(todo => todo.id !== id);
        setTodos(filteredTodos);
    }

    return (
        <div>
            <form onSubmit={(e) => handleClickAdd(e)}>
                <input onChange={(e) =>
                    setCurrentInput(e.target.value)} 
                    value={currentInput}/>
                <button type={"submit"}>add</button>
            </form>
            <div>
                {
                    todos.map(todo => {
                        return (
                            <TodoItem title={todo.content}
                                      id={todo.id}
                                      handleClickRemove= 
                                      {handleClickRemove}/>
                        );
                    })
                }
            </div>
        </div>
    );
}

To make the todos look a bit prettier, before adding the css we will add a delete icon. You can get it from my here.

components/TodoItem.js
import {useState} from "react";
import Image from "next/image";

export default function TodoItem({title, id, handleClickRemove}){
    const [done, setIsDone] = useState(false);

    const markDone = () => {
        setIsDone(!done);
    }

    return (
        <div onClick={markDone}>
            <h2>{title}</h2>
            <button onClick={() => handleClickRemove(id)}>
            <Image src={"/trashicon.svg"} 
            width={20} height={20} /></button>
        </div>
    );
}

Now we will get to the fun part and actually style our litte site.

pages/index.js
import TodoList from "../components/TodoList";
import styles from "./index.module.scss";

export default function IndexPage(){
    return (
        <div className={styles.container}>
            <h1>Todo List</h1>
            <TodoList />
        </div>
    );
}
pages/index.module.scss
.container {
  position: absolute;
  left: 50%;
  transform: translate(-50%, 0);

  margin-top: 10em;

  h1 {
    color: white;
    margin-bottom: 2em;
  }
}
components/TodoList.js
import {useState} from "react";
import TodoItem from "./TodoItem";
import styles from "./todolist.module.scss";

export default function TodoList() {
    const [todos, setTodos] = useState([]);
    const [currentInput, setCurrentInput] = useState("");

    const handleClickAdd = (e) => {
        e.preventDefault();

        if (currentInput === "") {
            //input validation
            return;
        }

        const newTodo = {
            id: currentInput + Math.random().toString(),
            content: currentInput
        }

        let _todos = todos;
        _todos.push(newTodo);

        setTodos(_todos)
        setCurrentInput("")
    }

    const handleClickRemove = (id) => {
        const filteredTodos = todos.filter(todo => todo.id !== id);
        setTodos(filteredTodos);
    }

    return (
        <div className={styles.container}>
            <form onSubmit={(e) => handleClickAdd(e)}>
                <input onChange={(e) =>
                    setCurrentInput(e.target.value)} 
                    value={currentInput}
                    placeholder={"todo..."}/>
                <button type={"submit"}>add</button>
            </form>
            <div className={styles.todos}>
                {
                    todos.map(todo => {
                        return (
                            <TodoItem title={todo.content}
                                      id={todo.id}
                                      handleClickRemove
                                      ={handleClickRemove}/>
                        );
                    })
                }
            </div>
        </div>
    );
}
components/TodoList.module.scss
.container {
  display: flex;
  flex-direction: column;
  row-gap: 2em;

  form {
    display: flex;
    column-gap: 2em;

    input {
      background: #2C2E33;
      border: none;
      height: 40px;
      width: 250px;
      border-radius: 10px;
      color: white;
      padding-left: 1em;
    }

    input:focus {
      outline: 2px solid #47428E;
    }

    button {
      width: 100px;
      border: none;
      background: #47428E;
      border-radius: 10px;
      color: white;
    }

    button:hover {
      transition: 0.3s;
      background: #3f3a7c;
    }
  }

  .todos {
    display: flex;
    flex-direction: column;
    row-gap: 1em;
  }
}
components/TodoItem.js
import {useState} from "react";
import Image from "next/image";
import styles from "./todoitem.module.scss";

export default function TodoItem({title, id, handleClickRemove}) {
    const [done, setIsDone] = useState(false);

    const markDone = () => {
        setIsDone(!done);
    }

    return (
        <div onClick={markDone} 
            className={done? styles.container: 
            styles.doneContainer}
            <h2>{title}</h2>
            <button onClick={() => handleClickRemove(id)}>
            <Image src={"/trashicon.svg"} 
            width={20} height={20}/>
            </button>
        </div>
    );
}
components/todoitem.module.scss
.container {
  background: #2C2E33;
  border-radius: 10px;
  display: flex;
  justify-content: space-between;
  padding: 0.5em 1em 0.5em 1em;
  align-items: center;

  h2 {
    color: white;
    font-size: 20px;
    font-weight: lighter;
  }

  button {
    background: #ce6767;
    height: 40px;
    width: 40px;
    border: none;
    border-radius: 10px;
  }
}

.doneContainer {
  background: #535761;
  border-radius: 10px;
  display: flex;
  justify-content: space-between;
  padding: 0.5em 1em 0.5em 1em;
  align-items: center;

  h2 {
    color: #2b2d31;
    text-decoration: line-through;
    font-size: 20px;
    font-weight: lighter;
  }

  button {
    display:none;
  }
}

And thats all for today. I hope you had fun making this little app. If you enjoyed it make sure to leave a like.


This content originally appeared on DEV Community and was authored by Fabian Bandini


Print Share Comment Cite Upload Translate Updates
APA

Fabian Bandini | Sciencx (2022-07-05T16:54:41+00:00) Create a Todo App with NextJS. Retrieved from https://www.scien.cx/2022/07/05/create-a-todo-app-with-nextjs/

MLA
" » Create a Todo App with NextJS." Fabian Bandini | Sciencx - Tuesday July 5, 2022, https://www.scien.cx/2022/07/05/create-a-todo-app-with-nextjs/
HARVARD
Fabian Bandini | Sciencx Tuesday July 5, 2022 » Create a Todo App with NextJS., viewed ,<https://www.scien.cx/2022/07/05/create-a-todo-app-with-nextjs/>
VANCOUVER
Fabian Bandini | Sciencx - » Create a Todo App with NextJS. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/07/05/create-a-todo-app-with-nextjs/
CHICAGO
" » Create a Todo App with NextJS." Fabian Bandini | Sciencx - Accessed . https://www.scien.cx/2022/07/05/create-a-todo-app-with-nextjs/
IEEE
" » Create a Todo App with NextJS." Fabian Bandini | Sciencx [Online]. Available: https://www.scien.cx/2022/07/05/create-a-todo-app-with-nextjs/. [Accessed: ]
rf:citation
» Create a Todo App with NextJS | Fabian Bandini | Sciencx | https://www.scien.cx/2022/07/05/create-a-todo-app-with-nextjs/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.