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
fabianbandini / todo-app-tutorial
Source Code for my tutorial on dev.to
Source Code for tutorial on dev.to/fabianbandini Demo: https://todo-app-tutorial-f4p029hcs-fabianbandini.vercel.app/
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
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
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/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.