Day 5: Building a Notes Keeping App πwith React JS


Welcome to Day 5 of the 21-Day Frontend Development Challenge! Today, I am excited to share my progress on building a Notes Keeping App using React JS. ππ» This app provides a convenient way to create, edit, and manage notes effortlessly. Let's dive into what I accomplished! π
Github Repo: Notes Keeping App
Live Demo Url: https://notes-keeper-21day.netlify.app/
Project Overview π
The Notes Keeping App is a practical and efficient tool that allows users to keep track of their notes. With this app, users can create new notes, delete unwanted ones, and edit existing notes. The app also features search functionality to help users find specific notes based on the title or content. The notes are stored persistently using local storage, ensuring that users can access their notes even after closing the app. πποΈ
Technologies Used
To build the Notes Keeping App, I utilized the following technologies and libraries:
React A popular JavaScript library for building user interfaces.
React Icons: A library that provides a set of customizable icons for React applications.
uuid: A package for generating unique IDs.
react-toastify : A library for displaying toast notifications in React applications.
Features of the Notes-Keeping App
The Notes Keeping App offers a range of features to ensure an optimal note-taking experience:
Note Creation: Users can easily create a new note by providing a title and content for the note.
Note Listing: The app displays a list of all the created notes, allowing users to browse and manage them efficiently.
Note Editing: Users can edit the title and content of a note by selecting it from the list and making the desired changes.
Note Deletion: The app enables users to delete a note, removing it from the list and the database.
Search Functionality: Users can search for specific notes by entering keywords in the search bar, making it easier to find relevant information.
Toast Notifications: The app incorporates toast notifications using the
react-toastify
library to provide feedback to users when they perform actions such as adding, deleting, or updating notes.
What I Learned π§ π‘
Throughout the development of the Notes Keeping App, I gained valuable insights and knowledge that will enhance my front-end development skills. Here are some key takeaways:
Enhanced understanding of React hooks, particularly useState and useEffect, for managing component state and handling side effects. βοΈ
Improved skills in handling user input, form submission, and data manipulation, which are essential for building interactive web applications. βοΈπ
Deepened knowledge of working with local storage to store and retrieve data in a React application. This allows for seamless user experience and data persistence. πΎπ
Strengthened ability to develop user-friendly interfaces and implement essential features such as note creation, deletion, and search functionality. π₯οΈπ
Project Showcase and Demo ππ·
To see the Notes Keeping App in action, check out the live demo [here](insert-live demo-link). The demo showcases the app's features, including adding new notes, editing existing notes, deleting notes, and performing searches. It provides a glimpse into the user interface and the seamless functionality of the app.
Here are some screenshots from the Notes Keeping App:
- Notes Keeping App Initial View
- After Adding Notes
Development Flow ππ»
Let's take a closer look at the development flow of the Notes Keeping App to understand how the different components work together. Here's the step-by-step guide:
π Step 1: Create a React Project
To begin, make sure you have Node.js installed on your machine. Open your terminal and execute the following command to create a new React project:
npx create-react-app notes-keeping-app
This command sets up a new React project with the name "notes-keeping-app" in a folder of the same name.
π³ Step 2: Project Tree Overview
Let's take a look at the project tree structure to understand the organization of files and directories:
notes-keeping-app
βββ public
β βββ favicon.ico
β βββ index.html
β βββ manifest.json
βββ src
β βββ components
β β βββ NoteEditor.js
β β βββ NoteForm.js
β β βββ NoteList.js
β β βββ App.js
β βββ App.css
β βββ index.js
β βββ index.css
βββ .gitignore
βββ package-lock.json
βββ package.json
βββ README.md
The public
the directory contains the HTML template and other static assets. The src
directory houses the main application code, including components and styles.
π Step 3: App Component and Basic Setup
In the src
folder, open App.js
and replace the existing code with the following:
import React from 'react';
function App() {
return (
<div className="app">
<h1>Notes Keeping App π</h1>
</div>
);
}
export default App;
This code defines the App
component, which serves as the root component of our app. It renders a simple heading indicating the app name.
βοΈ Step 4: NoteForm Component
Next, let's create a form component that allows users to add new notes. Create a new file called NoteForm.js
in the components
directory and add the following code:
import React from 'react';
function NoteForm() {
return (
<form className="note-form">
<input type="text" placeholder="Note Title" />
<textarea placeholder="Note Content"></textarea>
<button type="submit">Add Note</button>
</form>
);
}
export default NoteForm;
The NoteForm
component renders an HTML form with input fields for the note title and content, along with a submit button.
ποΈ Step 5: NoteList Component
Now, let's create a component to display a list of notes. Create a new file called NoteList.js
in the components
directory and add the following code:
import React from 'react';
function NoteList() {
return (
<div className="note-list">
<h2>My Notes π</h2>
<ul>
<li>Note 1</li>
<li>Note 2</li>
<li>Note 3</li>
</ul>
</div>
);
}
export default NoteList;
The NoteList
the component renders a heading and an unordered list of dummy notes. Later, we'll replace this static data with dynamic content.
ποΈ Step 6: Complete App Component
Now, let us have the NoteForm
and NoteList
components, let's integrate them into the App
component. Open the App.js
file and update the code as follows:
import React from 'react';
import NoteForm from './components/NoteForm';
import NoteList from './components/NoteList';
function App() {
return (
<div className="app">
<h1>Notes Keeping App π</h1>
<NoteForm />
<NoteList />
</div>
);
}
export default App;
Here, we import the NoteForm
and NoteList
components and render them within the App
component.
βοΈ Step 7: Styling the App Component
Let's add some basic styling to the App
component. Open the App.css
file in the same directory and update the code with the following styles:
Final CSS File of the Note Editor: Access from here
.app {
max-width: 600px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
h1 {
font-size: 28px;
margin-bottom: 20px;
}
.note-form input,
.note-form textarea,
.note-form button {
margin-bottom: 10px;
width: 100%;
padding: 10px;
font-size: 16px;
}
.note-list {
margin-top: 40px;
}
.note-list h2 {
font-size: 24px;
}
.note-list ul {
list-style-type: none;
padding: 0;
}
.note-list li {
margin-bottom: 10px;
}
These styles define the layout and appearance of the App
component, the NoteForm
, and the NoteList
.
π Step 8: Running the Application
You can now start the development server and see the Notes Keeping App in action. In the terminal, navigate to the project's root directory and run the following command:
npm start
The app will be available at http://localhost:3000, and you should see the Notes Keeping App with the form and a list of static notes.
That's it for the initial setup of the app! In the next steps, we'll add functionality to add and display dynamic notes, and also implement local storage to persist the data.
π Step 9: Managing Notes with State
In order to manage the notes, we'll introduce the state using React's useState
hook. Update the App
component code as follows:
import React, { useState } from 'react';
import NoteForm from './components/NoteForm';
import NoteList from './components/NoteList';
function App() {
const [notes, setNotes] = useState([]);
const addNote = (title, content) => {
const newNote = {
id: Date.now(),
title,
content,
};
setNotes([...notes, newNote]);
};
return (
<div className="app">
<h1>Notes Keeping App π</h1>
<NoteForm addNote={addNote} />
<NoteList notes={notes} />
</div>
);
}
export default App;
Here, we define the notes
state variable using the useState
hook and initialize it with an empty array. We also create the addNote
function, which receives the title and content of the note and adds it to the notes
state array using the setNotes
function.
ποΈ Step 10: Updating the Note
We'll now implement the functionality to update an existing note. Update the App
component code as follows:
import React, { useState } from 'react';
import NoteForm from './components/NoteForm';
import NoteList from './components/NoteList';
function App() {
const [notes, setNotes] = useState([]);
const addNote = (title, content) => {
const newNote = {
id: Date.now(),
title,
content,
};
setNotes([...notes, newNote]);
};
const updateNote = (id, updatedTitle, updatedContent) => {
const updatedNotes = notes.map((note) => {
if (note.id === id) {
return {
...note,
title: updatedTitle,
content: updatedContent,
};
}
return note;
});
setNotes(updatedNotes);
};
return (
<div className="app">
<h1>Notes Keeping App π</h1>
<NoteForm addNote={addNote} />
<NoteList notes={notes} updateNote={updateNote} />
</div>
);
}
export default App;
In the App
component, we define the updateNote
the function that takes the id
, updatedTitle
, and updatedContent
as parameters. It then maps over the notes
array and updates the note with the corresponding id
. Finally, the setNotes
function is used to update the state with the modified notes
array.
ποΈ Step 11: NoteForm Component
Let's update the NoteForm
component to include the functionality of adding and updating notes. Open the NoteForm.js
file and update the code as follows:
import React, { useState } from 'react';
import { toast, ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
function NoteForm({ addNote }) {
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (title && content) {
addNote(title, content);
setTitle('');
setContent('');
} else if (!title && !content) {
// Show a warning toast notification for both fields being blank
toast.warn('Title and content are required.');
} else if (!title) {
// Show a warning toast notification for the title field being blank
toast.warn('Title is required.');
} else {
// Show a warning toast notification for the content field being blank
toast.warn('Content is required.');
}
};
return (
<div>
<form className="note-form" onSubmit={handleSubmit}>
<input
type="text"
placeholder="Title"
className="note-input"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<textarea
placeholder="Content"
className="note-textarea"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
<button type="submit" className="note-button-add">Add Note</button>
</form>
<ToastContainer position="top-right" autoClose={3000} hideProgressBar />
</div>
);
}
export default NoteForm;
Here, we include the editingNote
prop in the NoteForm
component to determine whether we're in edit mode or add mode. The form's submit event is handled by the handleSubmit
function, which checks if there's a editingNote
value. If there is, it calls the updateNote
function; otherwise, it calls the addNote
function. After submitting the form, the input fields are cleared.
π Step 12: NoteEditor Component
Next, we'll update the NoteEditor
component to display the notes and enable editing. Open the NoteEditor.js
file and update the code as follows:
import React, { useState } from 'react';
function NoteEditor({ note, updateNote, cancelEdit }) {
const [editedTitle, setEditedTitle] = useState(note.title);
const [editedContent, setEditedContent] = useState(note.content);
const handleTitleChange = (e) => {
setEditedTitle(e.target.value);
};
const handleContentChange = (e) => {
setEditedContent(e.target.value);
};
const saveNote = () => {
updateNote(note.id, editedTitle, editedContent);
};
return (
<div className="note-editor">
<input
type="text"
value={editedTitle}
onChange={handleTitleChange}
className="note-input"
/>
<textarea
value={editedContent}
onChange={handleContentChange}
className="note-textarea"
></textarea>
<div className="buttons">
<button className="cancel-button" onClick={cancelEdit}>
Cancel
</button>
<button className="save-button" onClick={saveNote}>
Save
</button>
</div>
</div>
);
}
export default NoteEditor;
The NoteEditor
the component is responsible for editing an existing note. It receives three props: note
, updateNote
, and cancelEdit
.
Inside the component, we use the useState
hook to define two state variables: editedTitle
and editedContent
. These variables hold the edited values of the note's title and content, respectively. We initialize them with the initial values from the note
prop.
Next, we define two event handler functions: handleTitleChange
and handleContentChange
. These functions update the editedTitle
and editedContent
state variables, respectively, whenever the input fields for the title and content change.
The saveNote
the function is called when the user clicks the "Save" button. It invokes the updateNote
function (passed as a prop) with the updated note's ID, title, and content. This function will handle updating the note in the main app component.
π Step 13: NoteList Component
Next, we'll update the NoteList
component to display the notes and enable editing and deletion. Open the NoteList.js
file and update the code as follows:
import React from 'react';
import {FaEdit, FaTrashAlt} from "react-icons/fa";
function NoteList({ notes, deleteNote, setEditingNote }) {
const handleEditNote = (note) => {
setEditingNote(note);
};
const formatDate = (dateString) => {
const date = new Date(dateString);
return date.toLocaleString();
};
const getRandomColor = () => {
const colors = ['#8db5e1', '#ffb6c1', '#f2e593', '#b7e2b2'];
const randomIndex = Math.floor(Math.random() * colors.length);
return colors[randomIndex];
};
return (
<div className="note-list">
{notes.map((note) => (
<div className="note" key={note.id} style={{ backgroundColor: getRandomColor() }}>
<h2>{note.title}</h2>
<p>{note.content}</p>
<p className="note-date">
{note.updatedAt ? (
`Last Updated: ${formatDate(note.updatedAt)}`
) : (
`Created At: ${formatDate(note.createdAt)}`
)}
</p>
<div className="note-buttons">
<button className="edit-button" onClick={() => handleEditNote(note)}>
Edit <FaEdit className='icon'/>
</button>
<button className="delete-button" onClick={() => deleteNote(note.id)}>
Delete <FaTrashAlt className='icon'/>
</button>
</div>
</div>
))}
</div>
);
}
export default NoteList;
In the NoteList
component, we map over the notes
array and render each note as an <li>
element. We display the note's title and content and include buttons for editing and deleting the note. The deleteNote
function is called the notes id
when the delete button is clicked, and the setEditingNote
function is called the note object when the edit button is clicked.
π₯ Step 13: Implementing Local Storage and Final App.js
To persist the notes even after refreshing the page, we'll use the browser's local storage. Update the App
component code as follows:
import React, { useState, useEffect } from 'react';
import {FaRegStickyNote} from "react-icons/fa";
import { v4 as uuidv4 } from 'uuid';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import NoteForm from './components/NoteForm';
import NoteList from './components/NoteList';
import NoteEditor from './components/NoteEditor';
function App(){
const [notes, setNotes] = useState([]);
const [searchQuery, setSearchQuery] = useState('');
const [editingNote, setEditingNote] = useState(null);
const addNote = (title, content) => {
const newNote = {
id: uuidv4(), // Generate a unique ID for the note
title,
content,
createdAt: new Date().toISOString(),
updatedAt: null,
};
const updatedNotes = [...notes, newNote];
setNotes(updatedNotes);
localStorage.setItem('notes', JSON.stringify(updatedNotes));
// Show a success toast notification after adding a note
toast.success('Note added successfully!');
};
const deleteNote = (index) => {
const updatedNotes = [...notes];
updatedNotes.splice(index, 1);
setNotes(updatedNotes);
localStorage.setItem('notes', JSON.stringify(updatedNotes));
// Show a Warn toast notification after deleting a note
toast.error('Note deleted successfully!');
};
const updateNote = (id, title, content) => {
const updatedNotes = notes.map((note) => {
if (note.id === id) {
return {
...note,
title,
content,
updatedAt: new Date().toISOString(),
};
}
return note;
});
setNotes(updatedNotes);
setEditingNote(null);
localStorage.setItem('notes', JSON.stringify(updatedNotes));
// Show a success toast notification after updating a note
toast.success('Note updated successfully!');
};
const cancelEdit = () => {
setEditingNote(null);
};
useEffect(() => {
const storedNotes = localStorage.getItem('notes');
if (storedNotes) {
setNotes(JSON.parse(storedNotes));
}
}, []);
const filteredNotes = notes.filter(
(note) =>
note.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
note.content.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<div className="app">
<h1>Notes Keeping App <FaRegStickyNote className='icon'/></h1>
{editingNote ? (
<NoteEditor
note={editingNote}
updateNote={updateNote}
cancelEdit={cancelEdit}
/>
) : (
<NoteForm addNote={addNote} />
)}
<div className='search-input-container'><input
type="text"
placeholder="Search"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="search-input"
/>
</div>
<NoteList notes={filteredNotes} deleteNote={deleteNote} setEditingNote={setEditingNote} />
<ToastContainer />
</div>
);
};
export default App;
Here, we use the useEffect
hook to load the stored notes from local storage when the component mounts. We parse the stored notes and update the state with the retrieved data. Additionally, we use another useEffect
hook to save the notes in local storage whenever the `notes state changes.
π₯ Step 14: Final Touches and Deployment
At this point, the basic functionality of the Notes Keeping App is complete. You can further enhance the app by adding additional features like sorting notes, adding labels or categories, etc. Additionally, you can apply more styles and customize the UI to make it visually appealing.
Github Repo: Notes Keeping App
Once you're satisfied with the app, you can deploy it to a hosting platform of your choice. Some popular options include Netlify, Vercel, GitHub Pages, and Heroku. Here are the general steps to deploy a React app:
- Build the app: In the terminal, navigate to the project's root directory and run the following command:
npm run build
This will create an optimized production build of your app in the build
directory.
Choose a hosting platform: Select a hosting platform that best suits your needs and create an account if required.
Deploy the app:
Netlify: Drag and drop the
build
folder to the Netlify dashboard or connect your repository to automate deployments.Vercel: Use the Vercel CLI or connect your repository to Vercel to deploy the app.
GitHub Pages: Push the
build
folder to a GitHub repository'sgh-pages
branch and enable GitHub Pages in the repository settings.
Each hosting platform may have its own deployment process, so make sure to refer to their documentation for detailed instructions.
π Congratulations! Your Notes Keeping App is now ready to be used and shared with others. ππ
Remember to regularly save your code and commit it to a version control system (such as Git) to track changes and collaborate with others effectively.
π Step 15: Conclusion
In this development flow, we have successfully created a Notes Keeping App using React. We started by setting up the development environment, creating a new React project, and understanding the project structure. Then, we implemented the core functionality of the app, such as adding notes, deleting notes, and updating notes.
Throughout the process, we leveraged various React concepts and features, including state management with useState
, side effects with useEffect
, and conditional rendering. We also utilized external libraries like react-icons
and uuid
to enhance the app's functionality.
We discussed the importance of organizing components into reusable and modular units to maintain a clean and scalable codebase. We created three main components: NoteForm
for adding new notes, NoteList
for displaying the list of notes, and NoteEditor
for editing existing notes.
Furthermore, we explored how to persist data using localStorage
to store and retrieve notes, allowing users to access their notes even after refreshing the app.
To improve the user experience, we implemented toast notifications using the react-toastify
library, providing feedback for successful note operations.
Finally, we discussed deployment options and provided a general guide for deploying a React app to popular hosting platforms like Netlify, Vercel, and GitHub Pages.
Enhancing the User Experience with Toast Notifications
To further improve the user experience, I added toast notifications using the react-toastify
library. Toast notifications provide immediate feedback to the user, informing them about the outcome of their actions. Let's explore how toast notifications were integrated into the Notes Keeping App:
Adding a Note with Toast Notification βοΈπ
When a user adds a new note, a toast notification is displayed to indicate the successful addition of the note. This immediate feedback assures the user that their action was successful. Here's the code snippet for adding the toast notification:
const addNote = (title, content) => {
// ...
// Show a success toast notification after adding a note
toast.success('Note added successfully!');
};
By utilizing the toast.success
function from the react-toastify
library, a success toast notification is triggered, displaying a message that confirms the successful addition of the note.
Deleting a Note with Toast Notification ποΈβ
Similarly, when a user deletes a note, a toast notification is displayed to indicate the successful deletion of the note. This confirmation notification assures the user that the note has been successfully removed. Here's the code snippet for adding the toast notification:
const deleteNote = (index) => {
// ...
// Show a Warn toast notification after deleting a note
toast.error('Note deleted successfully!');
};
Using the toast.error
function, a warning toast notification is triggered, displaying a message that confirms the successful deletion of the note.
Updating a Note with Toast Notification πβ¨
When a user updates a note, a toast notification is displayed to indicate the successful update of the note. This notification reassures the user that their changes have been saved. Here's the code snippet for adding the toast notification:
const updateNote = (id, title, content) => {
// ...
// Show a success toast notification after updating a note
toast.success('Note updated successfully!');
};
By utilizing the toast.success
function once again, a success toast notification is triggered, displaying a message that confirms the successful update of the note.
User Experience with Toast Notifications ππ‘
By incorporating toast notifications into the Notes Keeping App, the overall user experience is significantly improved. Users receive immediate feedback when performing actions such as adding, deleting, or updating notes, ensuring that they are aware of the status of their actions. The toast notifications provide a visually appealing and informative way to communicate the outcome of these actions.
The react-toastify
library offers various customization options, allowing you to tailor the appearance and behavior of the toast notifications to align with the overall design of your app. You can choose different toast types, durations, and positions, and even customize the appearance using CSS.
Conclusion and Next Steps π―
The addition of toast notifications has taken the Notes Keeping App to the next level in terms of user experience. Users can now receive instant feedback when performing actions, making their interaction with the app more seamless and satisfying.
As part of my 21-day frontend development challenge, I plan to continue building on this app by implementing additional features such as sorting notes, adding categories or tags, and incorporating user authentication to ensure data privacy. I also aim to optimize the app's performance and responsiveness for a better user experience across different devices.
Thank you for joining me on this exciting journey of front-end development! ππ» Don't forget to follow me on LinkedIn for more updates, insights, and code breakdowns.
Let's keep coding and learning together! π»π
Happy coding! π»π
Subscribe to my newsletter
Read articles from Vinish Bhaskar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Vinish Bhaskar
Vinish Bhaskar
Passionate Frontend Developer with a strong focus on ReactJS. Extensive experience in freelance web development, delivering 20+ SEO-optimized WordPress websites. Skilled in creating dynamic user interfaces using ReactJS, JavaScript, and CSS. Seeking immediate front-end opportunities to contribute expertise in ReactJS, JavaScript, CSS, and WordPress. Let's connect and discuss further details.