This detailed guide explains how to make a personalized notes app using HTML, CSS, and JavaScript. Create and store notes in your browser easily.
Table of Contents
Creating a notes app using HTML, CSS, and JavaScript is an easy and fun project, especially if you’re starting with web development. This notes app lets users add, edit, delete, and save notes. It also uses localStorage, so your notes stay available even after refreshing the page.
In this tutorial, you’ll learn how to build a basic notes app step-by-step with a simple interface and core features like:
- Adding a note
- Editing a note
- Deleting a note
- Saving notes using localStorage
Before we dive into the code, let’s quickly go through the tools and prerequisites required for this project.
Prerequisites
To create the notes app, you will need:
- Text Editor: Use VS Code or any code editor.
- Web Browser: Google Chrome, Firefox, or any modern browser.
- Basic knowledge of HTML, CSS, and JavaScript.
Source Code
Step 1 (HTML Code):
The HTML part will contain the layout of the notes app, including an input field, buttons, and a container for displaying notes. Here’s a breakdown of the HTML code:
1. DOCTYPE Declaration
<!DOCTYPE html>
- Specifies that this is an HTML5 document.
2. <html>
Tag
<html lang="en">
- Represents the root of the HTML document.
- The
lang="en"
attribute declares the language as English, helping browsers and search engines understand the content.
3. <head>
Section
The <head>
section contains meta-information and resources that the webpage needs to load.
a. Character Encoding
<meta charset="UTF-8">
- Ensures the webpage supports all characters (like emojis and special symbols) by using the UTF-8 encoding.
b. Viewport Meta Tag
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- Makes the page responsive by ensuring it scales properly on different screen sizes, especially mobile devices.
c. Title
<title>Notes App</title>
- Defines the title that appears on the browser tab.
d. CSS Link
<link rel="stylesheet" href="styles.css">
- Links an external CSS file (
styles.css
) that contains the styles for this page.
4. <body>
Section
This is where the content of the webpage is defined.
a. Main Container
<div class="container">
- A wrapper
<div>
element with the classcontainer
that holds the entire content.
b. Header Section
<header>
<h1>📝 Notes</h1>
<button id="themeToggle">🌙</button>
</header>
- Displays the page title as “Notes” along with a notepad emoji.
- Includes a button with an ID
themeToggle
and a moon emoji 🌙, likely to toggle between light and dark modes.
c. Note Input Section
<div class="note-input">
<textarea id="newNote" placeholder="Type your note here..."></textarea>
<button id="addNoteBtn">Add Note</button>
</div>
- Contains:
- A
<textarea>
where the user types a new note, with a placeholder"Type your note here..."
. - A button with the ID
addNoteBtn
to add the new note.
- A
d. Notes Display Area
<div id="notesContainer" class="notes-container">
<!-- Notes will appear here dynamically -->
</div>
- An empty
<div>
with the IDnotesContainer
where notes will be displayed dynamically (likely using JavaScript).
5. JavaScript Inclusion
<script src="script.js"></script>
- Links an external JavaScript file (
script.js
) to add functionality, such as:- Adding notes dynamically.
- Handling theme toggling.
Step 2 (CSS Code):
We’ll use basic CSS to make the app look neat and user-friendly. Here’s a detailed explanation of the CSS code:
1. Global Styles (*
)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Arial', sans-serif;
}
*
targets all elements, applying:margin: 0
andpadding: 0
: Removes default margins and padding.box-sizing: border-box
: Ensures padding and borders are included within the element's width/height.font-family: 'Arial', sans-serif
: Sets a default font.
2. body
Styles
body {
background-color: #f0f0f0;
color: #333;
transition: background-color 0.3s, color 0.3s;
}
- Sets a light gray background (
#f0f0f0
) and dark gray text (#333
). transition
ensures smooth changes when switching colors (e.g., for theme toggling).
3. Container Styling
.container {
max-width: 600px;
margin: 50px auto;
padding: 20px;
background-color: #fff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 10px;
}
- Centers the container on the screen with:
max-width: 600px
: Limits the width.margin: 50px auto
: Adds vertical space and centers horizontally.box-shadow
: Adds a subtle shadow for an elevated look.border-radius: 10px
: Rounds the corners.
4. Header Styling
header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
- Uses Flexbox to align the title and theme toggle button side-by-side.
justify-content: space-between
pushes them to opposite ends.
5. Theme Toggle Button
#themeToggle {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
}
- Removes the default background and border.
- Increases font size to 1.5rem (for better visibility).
cursor: pointer
changes the cursor to a hand icon on hover.
6. Note Input Section
.note-input {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
- Flexbox layout ensures the textarea and button are aligned horizontally.
gap: 10px
adds space between the input elements.
7. Textarea Styling
textarea {
flex: 1;
resize: none;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
flex: 1
makes the textarea take available space.resize: none
disables resizing.- Adds padding, border, and rounded corners.
8. Add Note Button
#addNoteBtn {
padding: 10px 20px;
border: none;
background-color: #28a745;
color: #fff;
cursor: pointer;
border-radius: 5px;
}
#addNoteBtn:hover {
background-color: #218838;
}
- Styled with a green background (
#28a745
). - On hover, the background becomes a darker green (
#218838
).
9. Notes Container
.notes-container {
display: flex;
flex-direction: column;
gap: 10px;
}
- Uses Flexbox with
flex-direction: column
to stack notes vertically. - Adds
gap: 10px
between notes.
10. Individual Note Styling
.note {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #fafafa;
display: flex;
justify-content: space-between;
align-items: center;
}
- Each note has:
- Padding, border, and rounded corners.
- A light gray background (
#fafafa
). - Flexbox layout to align the content horizontally.
11. Note Content and Button Styling
.note p {
flex: 1;
margin-right: 10px;
word-break: break-word;
}
flex: 1
allows the paragraph to use remaining space.word-break: break-word
ensures long text wraps properly.
.note button {
background-color: #dc3545;
color: #fff;
border: none;
padding: 5px 10px;
border-radius: 5px;
cursor: pointer;
}
.note button:hover {
background-color: #c82333;
}
- Delete button is styled with a red background (
#dc3545
). - On hover, it becomes a darker red (
#c82333
).
12. Dark Theme Styling
body.dark {
background-color: #333;
color: #f0f0f0;
}
body.dark .container {
background-color: #444;
}
body.dark textarea {
background-color: #555;
color: #fff;
border: 1px solid #666;
}
body.dark .note {
background-color: #666;
}
- Dark theme is activated when the
body
has the classdark
. - The background, text, and elements are adjusted to darker shades.
13. Additional Note Styles
.note button {
margin-left: 5px;
}
.note pre {
white-space: pre-wrap;
word-break: break-word;
margin: 0;
flex: 1;
}
pre
tags inside notes preserve whitespace but wrap lines (usingpre-wrap
).word-break: break-word
ensures long words don’t overflow.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Arial', sans-serif;
}
body {
background-color: #f0f0f0;
color: #333;
transition: background-color 0.3s, color 0.3s;
}
.container {
max-width: 600px;
margin: 50px auto;
padding: 20px;
background-color: #fff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 10px;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
h1 {
font-size: 2rem;
}
#themeToggle {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
}
.note-input {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
textarea {
flex: 1;
resize: none;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
#addNoteBtn {
padding: 10px 20px;
border: none;
background-color: #28a745;
color: #fff;
cursor: pointer;
border-radius: 5px;
}
#addNoteBtn:hover {
background-color: #218838;
}
.notes-container {
display: flex;
flex-direction: column;
gap: 10px;
}
.note {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #fafafa;
display: flex;
justify-content: space-between;
align-items: center;
}
.note p {
flex: 1;
margin-right: 10px;
word-break: break-word;
}
.note button {
background-color: #dc3545;
color: #fff;
border: none;
padding: 5px 10px;
border-radius: 5px;
cursor: pointer;
}
.note button:hover {
background-color: #c82333;
}
body.dark {
background-color: #333;
color: #f0f0f0;
}
body.dark .container {
background-color: #444;
}
body.dark textarea {
background-color: #555;
color: #fff;
border: 1px solid #666;
}
body.dark .note {
background-color: #666;
}
.note button {
margin-left: 5px;
}
.note pre {
white-space: pre-wrap;
word-break: break-word;
margin: 0;
flex: 1;
}
Step 3 (JavaScript Code):
Now, let’s add the core functionality using JavaScript to allow adding, editing, deleting, and saving notes. Here's a detailed explanation of the JavaScript code:
1. Variable Initialization
const addNoteBtn = document.getElementById('addNoteBtn');
const newNote = document.getElementById('newNote');
const notesContainer = document.getElementById('notesContainer');
const themeToggle = document.getElementById('themeToggle');
let editingNoteIndex = null;
addNoteBtn
: Button for adding or updating a note.newNote
: Text area for entering the note content.notesContainer
: Div where all notes will be displayed.themeToggle
: Button to switch between light and dark themes.editingNoteIndex
: Keeps track of the index of the note being edited, if any.
2. Loading Saved Notes on Page Load
window.addEventListener('load', () => {
const savedNotes = JSON.parse(localStorage.getItem('notes')) || [];
savedNotes.forEach((note, index) => createNoteElement(note, index));
});
window.addEventListener('load')
: When the page loads, it retrieves saved notes from localStorage.localStorage.getItem('notes')
: Fetches saved notes (if any) from localStorage. If there are no notes, it returns an empty array.createNoteElement()
: For each saved note, a visual element (note card) is created and added to the UI.
3. Adding or Updating a Note
addNoteBtn.addEventListener('click', () => {
const noteText = newNote.value;
if (noteText.trim() === '') return;
if (editingNoteIndex !== null) {
updateNoteInLocalStorage(noteText, editingNoteIndex);
resetInput();
} else {
createNoteElement(noteText);
saveNoteToLocalStorage(noteText);
}
newNote.value = '';
});
addNoteBtn
triggers this function when clicked.- If the
newNote
text area is empty, it returns early to prevent saving an empty note. - If
editingNoteIndex
is notnull
, the user is updating an existing note:- Calls
updateNoteInLocalStorage()
to update the note in storage. - Resets the input field and button text using
resetInput()
.
- Calls
- If not editing, it creates a new note and saves it to localStorage using
saveNoteToLocalStorage()
.
4. Creating a Note Element
function createNoteElement(text, index = null) {
const noteDiv = document.createElement('div');
noteDiv.classList.add('note');
const noteText = document.createElement('pre');
noteText.textContent = text;
const editBtn = document.createElement('button');
editBtn.textContent = 'Edit';
editBtn.addEventListener('click', () => editNote(text, index || getNoteIndex(text)));
const deleteBtn = document.createElement('button');
deleteBtn.textContent = 'Delete';
deleteBtn.addEventListener('click', () => {
noteDiv.remove();
deleteNoteFromLocalStorage(text);
});
noteDiv.appendChild(noteText);
noteDiv.appendChild(editBtn);
noteDiv.appendChild(deleteBtn);
notesContainer.appendChild(noteDiv);
}
createNoteElement()
creates a visual note element with:- Text displayed using a
<pre>
element to preserve formatting. - Edit button to allow modifying the note.
- Delete button to remove the note from the UI and localStorage.
- Text displayed using a
- Note Editing: If the user clicks the edit button, the note is loaded into the input field for editing.
- Note Deletion: The delete button removes the note from the UI and storage.
5. Saving a New Note to LocalStorage
function saveNoteToLocalStorage(note) {
const notes = JSON.parse(localStorage.getItem('notes')) || [];
notes.push(note);
localStorage.setItem('notes', JSON.stringify(notes));
}
- Fetches existing notes from localStorage.
- Adds the new note to the array.
- Updates the localStorage with the new array.
6. Updating an Existing Note
function updateNoteInLocalStorage(newText, index) {
const notes = JSON.parse(localStorage.getItem('notes'));
notes[index] = newText;
localStorage.setItem('notes', JSON.stringify(notes));
// Refresh the UI
notesContainer.innerHTML = '';
notes.forEach((note, i) => createNoteElement(note, i));
editingNoteIndex = null;
}
- Updates the specified note in localStorage.
- Refreshes the UI by clearing and re-creating all notes.
- Resets the editing state by setting
editingNoteIndex
tonull
.
7. Deleting a Note from LocalStorage
function deleteNoteFromLocalStorage(note) {
const notes = JSON.parse(localStorage.getItem('notes')) || [];
const updatedNotes = notes.filter(n => n !== note);
localStorage.setItem('notes', JSON.stringify(updatedNotes));
}
- Filters out the deleted note from the notes array.
- Updates localStorage with the modified array.
8. Editing a Note
function editNote(text, index) {
newNote.value = text; // Load the note into the input field
editingNoteIndex = index; // Track the index for update
addNoteBtn.textContent = 'Update Note'; // Change button text
}
- Loads the selected note into the input field.
- Tracks the note’s index using
editingNoteIndex
for future updates. - Changes the button text to 'Update Note'.
9. Helper Function: Get Note Index
function getNoteIndex(text) {
const notes = JSON.parse(localStorage.getItem('notes'));
return notes.indexOf(text);
}
- Finds the index of a specific note in localStorage.
10. Resetting Input and Button Text
function resetInput() {
newNote.value = '';
addNoteBtn.textContent = 'Add Note';
}
- Clears the input field and resets the button text to 'Add Note'.
11. Theme Toggle (Dark/Light Mode)
themeToggle.addEventListener('click', () => {
document.body.classList.toggle('dark');
themeToggle.textContent = document.body.classList.contains('dark') ? '☀️' : '🌙';
});
- Toggles the dark mode class on the body.
- Changes the theme button's icon based on the current mode.
const addNoteBtn = document.getElementById('addNoteBtn');
const newNote = document.getElementById('newNote');
const notesContainer = document.getElementById('notesContainer');
const themeToggle = document.getElementById('themeToggle');
let editingNoteIndex = null;
window.addEventListener('load', () => {
const savedNotes = JSON.parse(localStorage.getItem('notes')) || [];
savedNotes.forEach((note, index) => createNoteElement(note, index));
});
addNoteBtn.addEventListener('click', () => {
const noteText = newNote.value;
if (noteText.trim() === '') return;
if (editingNoteIndex !== null) {
updateNoteInLocalStorage(noteText, editingNoteIndex);
resetInput();
} else {
createNoteElement(noteText);
saveNoteToLocalStorage(noteText);
}
newNote.value = '';
});
function createNoteElement(text, index = null) {
const noteDiv = document.createElement('div');
noteDiv.classList.add('note');
const noteText = document.createElement('pre');
noteText.textContent = text;
const editBtn = document.createElement('button');
editBtn.textContent = 'Edit';
editBtn.addEventListener('click', () => editNote(text, index || getNoteIndex(text)));
const deleteBtn = document.createElement('button');
deleteBtn.textContent = 'Delete';
deleteBtn.addEventListener('click', () => {
noteDiv.remove();
deleteNoteFromLocalStorage(text);
});
noteDiv.appendChild(noteText);
noteDiv.appendChild(editBtn);
noteDiv.appendChild(deleteBtn);
notesContainer.appendChild(noteDiv);
}
function saveNoteToLocalStorage(note) {
const notes = JSON.parse(localStorage.getItem('notes')) || [];
notes.push(note);
localStorage.setItem('notes', JSON.stringify(notes));
}
function updateNoteInLocalStorage(newText, index) {
const notes = JSON.parse(localStorage.getItem('notes'));
notes[index] = newText;
localStorage.setItem('notes', JSON.stringify(notes));
notesContainer.innerHTML = '';
notes.forEach((note, i) => createNoteElement(note, i));
editingNoteIndex = null;
}
function deleteNoteFromLocalStorage(note) {
const notes = JSON.parse(localStorage.getItem('notes')) || [];
const updatedNotes = notes.filter(n => n !== note);
localStorage.setItem('notes', JSON.stringify(updatedNotes));
}
function editNote(text, index) {
newNote.value = text;
editingNoteIndex = index;
addNoteBtn.textContent = 'Update Note';
}
function getNoteIndex(text) {
const notes = JSON.parse(localStorage.getItem('notes'));
return notes.indexOf(text);
}
function resetInput() {
newNote.value = '';
addNoteBtn.textContent = 'Add Note';
}
themeToggle.addEventListener('click', () => {
document.body.classList.toggle('dark');
themeToggle.textContent = document.body.classList.contains('dark') ? '☀️' : '🌙';
});
Final Output:
Conclusion:
Congratulations! You have successfully created a notes app using HTML, CSS, and JavaScript. This project is great for beginners and introduces essential concepts like:
- Using localStorage to save data.
- Creating dynamic content with JavaScript.
- Adding simple dark mode functionality.
With just a few lines of code, you now have a functional notes application. Try extending this project by adding features like search, categories, or a better user interface.
That’s a wrap!
I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.
Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee
And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!
Thanks!
Faraz 😊