Step-by-Step Implementation of Our Notes App

Step-by-Step Implementation of Our Notes App

In this module, we’ll build the Sticky Notes App step by step. We’ll scaffold the React project using Vite, understand the directory structure, create the main component, add note-taking features (add, edit, delete, pin), implement search and drag-and-drop reordering, and finally add export/import functionality along with instructions and styling. By the end of this module, you’ll have a fully functional Notes App ready to use and deploy.

1) Set up the environment

We’ll use Vite for faster development setup.

Open your terminal and run:

npm create vite@latest notes-app

Choose the following options:

  • Project name: notes-app
  • Framework: React
  • Variant: JavaScript

Now install dependencies and start the dev server:

cd notes-app

npm install

npm run dev

You’ll see the Vite + React starter page at a URL like:
http://localhost:5173/

2) Create Application Structure (Directory Structure)

After setup, your project will look like this:

notes-app/

├── index.html

├── package.json

├── vite.config.js

├── public/

└── src/

├── App.jsx

├── App.css

├── index.css

└── main.jsx

  • index.html: Has <div id="root"></div>, where React renders our app.
  • src/main.jsx: Entry file that mounts <App />.
  • src/App.jsx: Main component where we’ll add our notes app logic.
  • src/App.css & src/index.css: Styling files.

3) Rendering App in main.jsx

Open src/main.jsx. By default, it looks like this:

import React from "react";

import ReactDOM from "react-dom/client";

import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(

<React.StrictMode>

<App />

</React.StrictMode>

);

This mounts our <App /> component into the root element in index.html.

4) Building Notes Functionality in App.jsx

Open src/App.jsx. Here we’ll add the full logic of our notes app.

Step 1: Import dependencies

import { useState, useEffect } from "react";

import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";

import "./App.css";

  • useState: store notes, input text, search, and editing states.
  • useEffect: save notes automatically to localStorage.
  • react-beautiful-dnd: enable drag-and-drop ordering.

Step 2: Manage State

const [notes, setNotes] = useState(() => {

const saved = localStorage.getItem("notes");

return saved ? JSON.parse(saved) : [];

});

const [input, setInput] = useState("");

const [search, setSearch] = useState("");

const [editingId, setEditingId] = useState(null);

const [editText, setEditText] = useState("");

const [showInstructions, setShowInstructions] = useState(false);

  • notes: all notes stored as objects.
  • input: text for new note.
  • search: search filter.
  • editingId & editText: manage note editing.
  • showInstructions: control modal visibility.

We also define random colors for note backgrounds:

const colors = ["#ffda77", "#77ffda", "#ff77e9", "#a7ff77", "#77a7ff", "#ffb577"];

Step 3: Add and Save Notes

const addNote = () => {

if (input.trim() === "") return;

const newNote = {

id: Date.now().toString(),

text: input,

pinned: false,

color: colors[Math.floor(Math.random() * colors.length)],

};

setNotes([newNote, ...notes]);

setInput("");

};

  • Generates a unique id.
  • Assigns random background color.
  • Adds note at the top of the list.

Step 4: Edit, Pin, and Delete Notes

  • Edit

const startEditing = (id, text) => {

setEditingId(id);

setEditText(text);

};

const saveEdit = (id) => {

setNotes(notes.map((n) => n.id === id ? { ...n, text: editText } : n));

setEditingId(null);

setEditText("");

};

  • Double-click note to edit, press Enter or blur to save.
  • Pin

const togglePin = (id) => {

setNotes(notes.map((n) => n.id === id ? { ...n, pinned: !n.pinned } : n));

};

Pinned notes always stay highlighted.

  • Delete

const deleteNote = (id) => {

setNotes(notes.filter((n) => n.id !== id));

};

Step 5: Search and Sort Notes

const filteredNotes = notes

.filter((n) => n.text.toLowerCase().includes(search.toLowerCase()))

.sort((a, b) => (b.pinned === a.pinned ? 0 : b.pinned ? 1 : -1));

  • Filters by text.
  • Sorts pinned notes to the top.

Step 6: Drag-and-Drop Ordering

const handleDragEnd = (result) => {

if (!result.destination) return;

const items = Array.from(filteredNotes);

const [reorderedItem] = items.splice(result.source.index, 1);

items.splice(result.destination.index, 0, reorderedItem);

setNotes(items);

};

Drag notes horizontally to reorder them.

Step 7: Export and Import Notes

  • Export: Download all notes as JSON file.
  • Import: Upload a JSON file to restore notes.

const exportNotes = () => {

const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(notes, null, 2));

const downloadAnchorNode = document.createElement("a");

downloadAnchorNode.setAttribute("href", dataStr);

downloadAnchorNode.setAttribute("download", "notes.json");

document.body.appendChild(downloadAnchorNode);

downloadAnchorNode.click();

downloadAnchorNode.remove();

};

const importNotes = (e) => {

const fileReader = new FileReader();

fileReader.onload = (event) => {

try {

const importedNotes = JSON.parse(event.target.result);

setNotes(importedNotes);

} catch {

alert("Invalid JSON file!");

}

};

fileReader.readAsText(e.target.files[0]);

};

Step 8: Instructions Modal

We add a modal to guide users with all app features (add, edit, pin, delete, drag, export/import). Controlled by showInstructions state.

Step 9: UI Return Statement

We build the interface with:

  • Title + Instructions button
  • Note input field and Add button
  • Search bar
  • Export/Import buttons
  • Notes grid with drag-and-drop
  • Footer with credit
  • Instructions modal

5) Styling with CSS

All styles are inside App.css.

body {

background-color: #1e1e2f;

color: #f5f5f5;

font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;

margin: 0;

padding: 0;

}

.app {

text-align: center;

padding: 20px;

}

.title {

font-size: 2.5rem;

margin-bottom: 10px;

color: #ffda77;

}

.instructions-btn {

margin-bottom: 20px;

padding: 8px 15px;

border-radius: 8px;

border: none;

background: #77ddff;

color: #1e1e2f;

font-weight: bold;

cursor: pointer;

transition: 0.2s;

}

.instructions-btn:hover {

background: #55ccee;

transform: scale(1.05);

}

.note-input {

margin-bottom: 20px;

}

.note-input textarea {

width: 300px;

height: 80px;

padding: 10px;

border-radius: 10px;

border: none;

outline: none;

font-size: 1rem;

background: #2a2a40;

color: #f5f5f5;

resize: none;

}

.note-input button {

margin-left: 10px;

padding: 10px 20px;

border-radius: 8px;

border: none;

cursor: pointer;

background: #ff77e9;

color: #fff;

font-weight: bold;

transition: 0.2s;

}

.note-input button:hover {

background: #ff44cc;

transform: scale(1.05);

}

.search-bar {

padding: 10px;

width: 250px;

border-radius: 8px;

border: none;

margin-bottom: 20px;

font-size: 1rem;

background: #2a2a40;

color: #f5f5f5;

outline: none;

}

.export-import {

margin: 20px 0;

display: flex;

justify-content: center;

gap: 15px;

}

.export-import button {

padding: 8px 15px;

border-radius: 8px;

border: none;

background: #77ddff;

color: #1e1e2f;

font-weight: bold;

cursor: pointer;

transition: 0.2s;

}

.export-import button:hover {

background: #55ccee;

transform: scale(1.05);

}

.import-label {

padding: 8px 15px;

border-radius: 8px;

background: #ff77e9;

color: #fff;

font-weight: bold;

cursor: pointer;

transition: 0.2s;

display: inline-block;

position: relative;

}

.import-label:hover {

background: #ff44cc;

transform: scale(1.05);

}

.import-label input {

display: none;

}

.notes-container {

display: grid;

grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));

gap: 15px;

padding: 20px;

}

.note {

padding: 15px;

border-radius: 12px;

box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);

position: relative;

transition: transform 0.2s, box-shadow 0.2s;

cursor: grab;

}

.note p {

margin: 0;

word-wrap: break-word;

white-space: pre-wrap;

}

.note-buttons {

position: absolute;

top: 8px;

right: 8px;

display: flex;

gap: 5px;

}

.note-buttons button {

border: none;

background: transparent;

font-size: 1.1rem;

cursor: pointer;

}

.edit-textarea {

width: 100%;

min-height: 60px;

padding: 8px;

border-radius: 8px;

border: 1px solid #ccc;

resize: none;

font-size: 1rem;

background: #fff;

color: #333;

}

footer {

margin-top: 30px;

font-size: 0.9rem;

color: #aaa;

}

footer a {

color: #77ddff;

text-decoration: none;

}

footer a:hover {

text-decoration: underline;

}

.fade-in {

animation: fadeIn 0.4s ease-in-out;

}

@keyframes fadeIn {

from {

opacity: 0;

transform: scale(0.9) rotate(-2deg);

}

to {

opacity: 1;

transform: scale(1) rotate(0deg);

}

}

.modal-backdrop {

position: fixed;

top: 0;

left: 0;

width: 100%;

height: 100%;

background-color: rgba(0, 0, 0, 0.75);

display: flex;

justify-content: center;

align-items: center;

z-index: 100;

}

.modal-content {

background: #2a2a40;

color: #f5f5f5;

padding: 25px;

border-radius: 12px;

width: 90%;

max-width: 500px;

text-align: left;

position: relative;

}

.modal-content h2 {

text-align: center;

color: #ffda77;

}

.modal-content ul {

padding-left: 20px;

}

.modal-content button {

display: block;

margin: 20px auto 0 auto;

padding: 8px 15px;

border-radius: 8px;

border: none;

background: #77ddff;

color: #1e1e2f;

font-weight: bold;

cursor: pointer;

transition: 0.2s;

}

.modal-content button:hover {

background: #55ccee;

transform: scale(1.05);

}

Key highlights:

  • Dark background with light text for readability.
  • Notes styled with random pastel colors, pinned notes in pink.
  • Smooth hover effects on buttons.
  • Modal backdrop for instructions with centered content.
  • Drag-and-drop animations for notes (fade-in).

6) Final Touches and Deployment

  • Test all features: add, edit, delete, pin, search, drag, export/import.
  • Notes are automatically saved in localStorage.
  • Deploy by running:

npm run build

This generates a dist/ folder. Upload it to Netlify or Vercel for instant hosting.

Now your Sticky Notes App is complete. It supports persistent storage, searching, editing, pinning, drag-and-drop, and easy export/import of notes—all wrapped in a clean, responsive UI.