Build a Self-care Application Using Next.js and Appwrite
Part 2: Creating Calming Sounds in Our Appwrite and Next.js App
Introduction
In this tutorial series, we are exploring how to build a self-care app using Next.js, a popular React framework, and Appwrite, an open-source backend platform. In Part 1, we laid the foundation by setting up the project, implementing user authentication, and creating basic self-care components.
In Part 2, we will delve deeper into the development process by focusing on a new and exciting feature: Calming Sounds. We all know that soothing sounds can significantly contribute to relaxation and stress reduction. By leveraging Next.js, we will enhance our self-care app by integrating a feature that allows users to explore and play a collection of calming sounds.
Adding Our Sound Files And Creating CalmSounds Component
Let's start by creating public/sounds
folder and add our mp3 sound files to the folder.
Then in components/Calmsounds.js
add the following code:
import React, { useState } from 'react';
import Link from 'next/link';
const CalmSounds = () => {
const [sounds, setSounds] = useState([
{ id: 1, name: 'FirePlace', source: '/sounds/fireplace.mp3' },
{ id: 2, name: 'Crickets', source: '/sounds/crickets.mp3' },
{ id: 3, name: 'Bird Chirping', source: '/sounds/junglebirds.mp3' },
]);
const handlePlaySound = (id) => {
const updatedSounds = sounds.map((sound) => {
if (sound.id === id) {
return { ...sound, playing: true };
} else {
return { ...sound, playing: false };
}
});
setSounds(updatedSounds);
};
return (
<div className="calm-sounds">
<h2 className="component-title">Calm Sounds</h2>
<div className="sounds-container">
{sounds.map((sound) => (
<div
key={sound.id}
className={`sound ${sound.playing ? 'playing' : ''}`}
>
<div className="sound-name">{sound.name}</div>
<audio
src={sound.source}
controls={sound.playing}
autoPlay={sound.playing}
loop
/>
<button onClick={() => handlePlaySound(sound.id)}>Play</button>
</div>
))}
</div>
<div className="buttons-container">
<Link href="/Sounds">
<button>Explore More</button>
</Link>
</div>
</div>
);
};
export default CalmSounds;
In the CalmSounds
component:
The
useState
hook is used to define and initialize thesounds
state variable. It is an array of objects, where each object represents a sound with properties likeid
,name
, andsource
. The initial state includes three sounds.The
handlePlaySound
function is responsible for updating the state when a sound is played. It maps over thesounds
array and sets theplaying
property totrue
for the selected sound andfalse
for others. This ensures only one sound is played at a time.The
return
statement contains the JSX code that defines the component's structure and rendering logic.The component has a
<div>
container with the class name "calm-sounds".Inside the container, there's an
<h2>
element with the class name "component-title" displaying the title "Calm Sounds".Next, there's a
<div>
container with the class name "sounds-container" that holds the list of sounds.Inside the sounds container, the
sounds
array is mapped using themap
function to render each sound as a<div>
element with the class name "sound". Thesound.playing
class is conditionally added to highlight the currently playing sound.Each sound
<div>
contains the sound's name, an<audio>
element with thesrc
attribute pointing to the sound's source file, and play controls likecontrols
,autoPlay
, andloop
.Additionally, there's a "Play" button for each sound that triggers the
handlePlaySound
function when clicked, passing the sound'sid
as an argument.After the sounds container, there's another
<div>
container with the class name "buttons-container".Inside this container, there's a
<Link>
component from Next.js that wraps a "Explore More" button. Clicking the button will navigate to the "/Sounds" route.
Finally, the component is exported as the default export.
Export the Component to the Homepage
In the index.js
import our CalmSounds.js
component then render it within Home
parent component:
return (
<>
<Header />
<EncouragingQuotes />
<SelfCareList selfCareItems={selfCareItems} />
<CalmSounds />
Your homepage should now have this output:
Optimize the Caching of the Sound Files
To optimize the caching of the sound files in our self-care app, we can use the nextConfig
object and the headers
function provided by Next.js. By setting appropriate cache control headers, we can ensure that the sound files are cached by the user's browser, resulting in faster subsequent requests and improved performance.
To add this configuration to our Next.js project, add the following code into the next.config.js
file:
const nextConfig = {
async headers() {
return [
{
source: '/sounds/(.*)',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
];
},
};
module.exports = nextConfig;
This code sets the cache-control header for any request to the /sounds/
route and its subpaths. The max-age
directive is set to one year (31536000 seconds), indicating that the sound files can be cached by the browser for a long duration. The immutable
directive ensures that the cached files are not revalidated with the server until the specified max-age expires.
Creating Sounds.js File
Let's now create a sounds.js
file to add the rest of our sounds:
import React, { useState } from 'react';
import Header from './components/Header'
const CalmSounds = () => {
const [sounds, setSounds] = useState([
{ id: 1, name: 'Calm River', source: '/sounds/calm-river.mp3' },
{ id: 2, name: 'Forest', source: '/sounds/forest.mp3' },
{ id: 3, name: 'Bird Chirping', source: '/sounds/evening-birds.mp3' },
{ id: 4, name: 'Light Rain', source: '/sounds/light-rain.mp3' },
{ id: 5, name: 'Minor Arp', source: '/sounds/minor-arp.mp3' },
{ id: 6, name: 'Relaxing', source: '/sounds/relaxing.mp3' },
{ id: 7, name: 'Soft-rain', source: '/sounds/soft-rain.mp3' },
{ id: 8, name: 'Super Spacy', source: '/sounds/superspacy.mp3' },
{ id: 9, name: 'Piano', source: '/sounds/the-last-piano.mp3' },
{ id: 10, name: 'Uplifting', source: '/sounds/uplifting-pad.mp3' },
{ id: 11, name: 'Wind Chimes', source: '/sounds/wind-chimes.mp3' },
{ id: 12, name: 'Chicken Sounds', source: '/sounds/chicken-sounds.mp3' },
]);
const handlePlaySound = (id) => {
const updatedSounds = sounds.map((sound) =>
sound.id === id ? { ...sound, playing: true } : { ...sound, playing: false }
);
setSounds(updatedSounds);
};
return (
<div>
<Header />
{sounds.map((sound) => (
<div key={sound.id} className={`sound ${sound.playing ? 'playing' : ''}`}>
<div className="sound-name">{sound.name}</div>
<audio src={sound.source} controls={sound.playing} autoPlay={sound.playing} loop />
<button onClick={() => handlePlaySound(sound.id)}>Play</button>
</div>
))}
</div>
);
};
export default CalmSounds;
The CalmSounds
component renders a list of sound items with play buttons and audio controls. Here's an explanation of the code:
It includes the Header
component being imported and rendered. Additionally, the sounds
array is expanded with more sound objects. Here's a breakdown:
The
Header
component is imported from the './components/Header' file.The
sounds
array is expanded with additional sound objects. Each sound object has properties such asid
,name
, andsource
, representing a unique identifier, the name of the sound, and the source URL of the sound file, respectively. The array now contains twelve sound objects with different names and sources.The
handlePlaySound
function remains the same and is responsible for updating the state when a sound is played.In the return statement, the
Header
component is added before thesounds.map
function.Inside the
sounds.map
function, each sound is rendered as a<div>
element with the class name "sound". Thesound.playing
class is conditionally added to highlight the currently playing sound.Each sound
<div>
includes the sound's name, an<audio>
element with thesrc
attribute pointing to the sound's source file, and play controls likecontrols
,autoPlay
, andloop
.The "Play" button triggers the
handlePlaySound
function when clicked, passing the sound'sid
as an argument.
The CalmSounds
component now renders the Header
component and displays a list of sounds with play buttons. The sounds
array is iterated over using the map
function to render each sound element.
Adding the File to our Header
Now let's add our sound.js file in our Header
component:
<li>
<Link href="/Sounds" legacyBehavior>
CalmSounds
</Link>
</li>
Creating a Footer Component
Finally, let's create a footer component. In components
folder add a footer.js
file and add the following code:
import React from 'react';
const Footer = () => {
return (
<footer className="footer">
<div className="footer-content">
<p>© {new Date().getFullYear()} G-Care. All rights reserved.</p>
<p>Terms of Service | Privacy Policy</p>
</div>
</footer>
);
};
export default Footer;
Here's a breakdown of the code:
The
Footer
component is defined as a functional component using the arrow function syntax.The component returns JSX code that represents the structure and content of the footer.
The returned JSX code consists of a
<footer>
element with the class name "footer".Inside the
<footer>
element, there's a<div>
element with the class name "footer-content".Within the
<div>
element, there are two<p>
elements. The first<p>
element displays the current year using JavaScript'snew Date().getFullYear()
function and a copyright notice. The second<p>
element displays links for "Terms of Service" and "Privacy Policy".The content within the footer is static and doesn't include any dynamic behavior or interactions.
Finally, the
Footer
component is exported as the default export.
This Footer
component can be used in other parts of our application to render the footer section consistently by importing it and rendering it at the bottom of our code before our last div
.
Conclusion
In this tutorial, we explored how to enhance our self-care app by adding a Calming Sounds feature using Next.js. We created a CalmSounds component that allows users to explore and play a collection of calming sounds.
To optimize the caching of the sound files, we utilized Next.js' nextConfig object and the headers function to set appropriate cache control headers. By doing so, we ensured that the sound files are cached by the user's browser, resulting in faster subsequent requests and improved performance.
We also created a sounds.js file to expand the list of available sounds and added the component to our Header for easy access. Additionally, we created a Footer component to provide a consistent footer section throughout our application.
By following this tutorial series, we have built a self-care app with features like user authentication, self-care components, and the ability to explore and play calming sounds. This app can serve as a valuable tool for users to prioritize their mental well-being and practice self-care.