Sticky Notes Board React Project Tutorial with Code
Step-by-Step Implementation of Sticky Board
The goal of this module is to set up your environment, scaffold the app, and write the code step by step until we have a working sticky notes board running locally.
Set Up the Environment
Before coding, make sure your machine is ready:
- Install Node.js (LTS)
- Download from nodejs.org.
- Verify installation:
node -v
npm -v
- Install a Code Editor
- Use VS Code.
Once that’s done, open your terminal and create the project with Create React App:
npx create-react-app sticky-board
cd sticky-board
npm start
Your browser should open at http://localhost:3000 with the React default page. That means the setup worked.
Create Application Structure (Directory Layout)
Here’s the structure we’ll end up with inside src/:
src/
├─ components/
│ ├─ Board.js
│ ├─ Note.js
├─ utils.js
├─ App.js
├─ index.js
├─ index.css
- components/ → Holds all reusable UI pieces (Board and Note).
- utils.js → Helper functions for localStorage and ID generation.
- App.js → Main logic + form handling.
- index.css → Styling.
- index.js → Entry point.
Steps Needed to Build the Project
Let’s break down what we’ll actually do:
1. Create Utility Functions (src/utils.js)
In the src folder, create a file called utils.js and fill in the below code:
const STORAGE_KEY = 'sticky_notes_board_v1';
// Save notes to localStorage
export function saveNotes(notes) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(notes));
}
// Load notes from localStorage
export function loadNotes() {
const data = localStorage.getItem(STORAGE_KEY);
return data ? JSON.parse(data) : [];
}
// Generate a unique ID for each note
export function uid() {
return Math.random().toString(36).slice(2, 9);
}Explanation:
- saveNotes → stores notes as a JSON string in localStorage.
- loadNotes → retrieves notes or returns an empty array.
- uid → generates a random string to use as a unique ID.
2. Note Component (src/components/Note.js)
In the src folder, create another folder called components, and there, create a file called Note.js and then paste the below code:
import React, { useState } from 'react';
export default function Note({ note, onDelete, onUpdate }) {
const [editing, setEditing] = useState(false);
const [title, setTitle] = useState(note.title);
const [body, setBody] = useState(note.body);
function saveChanges() {
onUpdate({ ...note, title: title.trim(), body: body.trim(), updatedAt: Date.now() });
setEditing(false);
}
return (
<div className="note">
<div className="actions">
<button onClick={() => setEditing(!editing)}>
{editing ? 'Cancel' : 'Edit'}
</button>
<button onClick={() => onDelete(note.id)}>Delete</button>
</div>
{editing ? (
<>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Title"
/>
<textarea
value={body}
onChange={(e) => setBody(e.target.value)}
placeholder="Write your note..."
/>
<button onClick={saveChanges}>Save</button>
</>
) : (
<>
<h3>{note.title || 'Untitled'}</h3>
<small>{new Date(note.updatedAt || note.createdAt).toLocaleString()}</small>
<p>{note.body}</p>
</>
)}
</div>
);
}Explanation:
- Supports toggling between view and edit mode.
- onUpdate updates the note with new content.
- onDelete removes the note.
3. Board Component (src/components/Board.js)
Similarly, in the same folder, create another file called Board.js and fill in the code below:
import React from 'react';
import Note from './Note';
export default function Board({ notes, onDelete, onUpdate }) {
if (!notes.length) {
return <div className="empty">No notes yet. Add one above!</div>;
}
return (
<div className="board">
{notes.map((n) => (
<Note key={n.id} note={n} onDelete={onDelete} onUpdate={onUpdate} />
))}
</div>
);
}Explanation:
- Receives notes from App as props.
- Maps over notes → renders a Note component for each one.
- Shows a fallback message if no notes exist.
4. App Component (src/App.js)
Edit the App.js file in the src folder by pasting the below code:
import React, { useEffect, useState } from 'react';
import Board from './components/Board';
import { saveNotes, loadNotes, uid } from './utils';
function App() {
const [notes, setNotes] = useState([]);
const [title, setTitle] = useState('');
const [body, setBody] = useState('');
// Load notes from localStorage on first render
useEffect(() => {
setNotes(loadNotes());
}, []);
// Save notes to localStorage whenever they change
useEffect(() => {
saveNotes(notes);
}, [notes]);
function addNote(e) {
e.preventDefault();
if (!title.trim() && !body.trim()) return;
const now = Date.now();
const newNote = {
id: uid(),
title: title.trim(),
body: body.trim(),
createdAt: now,
updatedAt: now,
};
setNotes([newNote, ...notes]);
setTitle('');
setBody('');
}
function deleteNote(id) {
setNotes(notes.filter((n) => n.id !== id));
}
function updateNote(updated) {
setNotes(notes.map((n) => (n.id === updated.id ? updated : n)));
}
return (
<div className="app">
<h1>Sticky Notes Board</h1>
<form className="form" onSubmit={addNote}>
<input
placeholder="Title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<textarea
placeholder="Write your note..."
value={body}
onChange={(e) => setBody(e.target.value)}
/>
<button type="submit">Add Note</button>
</form>
<Board notes={notes} onDelete={deleteNote} onUpdate={updateNote} />
</div>
);
}
export default App;Explanation:
- Manages state for notes, title, and body.
- useEffect loads notes on mount and saves them when updated.
- Provides addNote, deleteNote, and updateNote handlers.
- Renders the form and passes data/functions to Board.
5. Styling (src/index.css)
It is time to style up your sticky pad. In the index.css file, paste the below code:
/* Global styles */
body {
font-family: 'Segoe UI', Roboto, Arial, sans-serif;
background: #f0ece2 url("https://www.transparenttextures.com/patterns/gray-floral.png");
margin: 0;
padding: 0;
color: #333;
}
.app {
max-width: 1000px;
margin: 40px auto;
padding: 20px;
}
h1 {
text-align: center;
font-size: 2rem;
margin-bottom: 25px;
color: #222;
text-shadow: 1px 1px 0px rgba(0,0,0,0.05);
}
/* Form styling */
.form {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 25px;
background: #fff;
padding: 15px;
border-radius: 10px;
box-shadow: 0px 4px 10px rgba(0,0,0,0.1);
}
.form input,
.form textarea {
padding: 12px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 14px;
resize: none;
transition: border 0.2s ease;
}
.form input:focus,
.form textarea:focus {
outline: none;
border: 1px solid #1f7aef;
box-shadow: 0 0 4px rgba(31,122,239,0.3);
}
.form button {
align-self: flex-start;
padding: 10px 18px;
border: none;
background: #1f7aef;
color: white;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
transition: background 0.2s ease;
}
.form button:hover {
background: #1663c7;
}
/* Board layout */
.board {
display: flex;
flex-wrap: wrap;
gap: 18px;
justify-content: center;
}
/* Sticky note styles */
.note {
width: 220px;
min-height: 140px;
padding: 15px;
border-radius: 8px;
box-shadow: 0px 6px 12px rgba(0,0,0,0.15);
position: relative;
transform: rotate(-1deg);
transition: transform 0.15s ease, box-shadow 0.15s ease;
cursor: grab;
overflow-wrap: break-word;
}
/* Random sticky note background colors */
.note:nth-child(3n+1) {
background: #fff176; /* yellow */
}
.note:nth-child(3n+2) {
background: #ffab91; /* peach */
}
.note:nth-child(3n+3) {
background: #80deea; /* teal */
}
.note:hover {
transform: scale(1.05) rotate(0deg);
box-shadow: 0px 10px 18px rgba(0,0,0,0.25);
}
/* Note text */
.note h3 {
margin-top: 0;
font-size: 16px;
}
.note p {
font-size: 14px;
margin: 8px 0;
white-space: pre-wrap;
}
.note small {
font-size: 11px;
color: #444;
}
/* Action buttons */
.note .actions {
position: absolute;
top: 8px;
right: 8px;
display: flex;
gap: 6px;
}
.note button {
padding: 4px 8px;
border: none;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: background 0.2s ease;
}
.note button:hover {
opacity: 0.9;
}
.note button:nth-child(1) {
background: #ffd54f; /* edit */
}
.note button:nth-child(2) {
background: #ef5350; /* delete */
color: white;
}
/* Empty state */
.empty {
padding: 30px;
text-align: center;
color: #555;
font-style: italic;
border: 2px dashed #ccc;
border-radius: 10px;
background: #fff;
}Explanation:
- Makes notes look like sticky cards with a yellow background.
- Styles the form and board layout.
- Adds hover effects to buttons for better UX.
At this point, your Sticky Notes Board is complete and runs locally.
Run:
npm start
Go to http://localhost:3000 → Add, edit, delete notes, and refresh the page. Your notes should persist.











