Blog reads tells us how often our content has been read. They help us understand how much traffic our content has been getting and we can use this data to strategize and repurpose our content accordingly.
What we will be building
This post will discuss using the react-intersection-observer
library to implement a blog read counter that is incremented when a user has read 70 percent of our blog post. These blog reads are stored on a database. You donโt need a custom server to manage this.
GitHub URL
https://github.com/Iheanacho-ai/appwrite-blog-read-counter
This is a Next.js project bootstrapped with create-next-app
.
Getting Started
First, run the development server:
npm run dev
# or
yarn dev
Open http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying pages/index.js
. The page auto-updates as you edit the file.
API routes can be accessed on http://localhost:3000/api/hello. This endpoint can be edited in pages/api/hello.js
.
The pages/api
directory is mapped to /api/*
. Files in this directory are treated as API routes instead of React pages.
Learn More
To learn more about Next.js, take a look at the following resources:
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
You can check out the Next.js GitHub repository - your feedback and contributions are welcome!
Deploy on Vercel
The easiest way to deploy your Next.js app is to use the Vercel Platform fromโฆ
Prerequisites
To get the most out of this project, we require the following:
- A basic understanding of CSS, JavaScript, and React.jsDocker Desktop is installed on the computer, run the
docker -v
command to verify if we have docker desktop installed, if not, install it from here - An Appwrite instance running on our computer; check out this article to create a local Appwrite instance. We will use Appwriteโs powerful database service and experience to manage our analytics data.
Setting up our Next.js app
Next.js is an open-source React framework that enables us to build server-side rendered static web applications.
To create our Next.js app, we navigate to our preferred directory and run the terminal command below:
npx create-next-app@latest
# or
yarn create next-app
After creating our app, we change the directory to the project and start a development server with:
cd <name of our project>
npm run dev
To see our app, we go to http://localhost:3000.
Installing react-intersection-observer
React-intersection-observer is a react library that allows developers to monitor when an element enters or leaves the viewport.
To install the react-intersection-observer in our application, run this terminal code.
npm install react-intersection-observer --save
#or
yarn add react-intersection-observer
Installing Appwrite
Appwrite is an open-source, end-to-end, back-end server solution that allows developers to build applications faster.
To use Appwrite in our Next.js application, we install the Appwrite client-side SDK for web applications.
npm install appwrite
Creating a new Appwrite project
During the creation of the Appwrite instance, we specified what hostname and port we see our console. The default value is localhost:80.
We go to localhost:80 and create a new account to see our console.
On our console, there is a Create Project ****button. Click on it to start a new project.
Our project dashboard appears once we have created the project. At the top of the page, there is a Settings bar. Click it to access the Project ID and API Endpoint.
We copy the Project ID and API Endpoint, which we need to initialize the Appwrite Web SDK.
In our project's root directory, we create a utils.js
file to initialize our Web SDK.
import { Appwrite } from 'appwrite';
// Init your Web SDK
export const sdk = new Appwrite();
sdk
.setEndpoint('http://localhost/v1') // Your API Endpoint
.setProject('625d9071b3fc17c7bfb6') // Your project ID
;
Creating an anonymous user session
Appwrite requires a user to sign in before reading or writing to a database to enable safety in our application. However, they allow us to create an anonymous session for small projects like this.
We use a function to create an anonymous user session once this application mounts. We will run this createAnoymousSession
function later in this tutorial.
import { Appwrite } from 'appwrite';
// Init your Web SDK
export const sdk = new Appwrite();
sdk
.setEndpoint('http://localhost/v1') // Your API Endpoint
.setProject('625d9071b3fc17c7bfb6') // Your project ID
;
//Creating anonymous Session
export const createAnonymousSession = async() => {
try{
await sdk.account.createAnonymousSession();
}catch(err){
console.log(err)
}
}
Creating the collection and attributes
In the Appwrite web Console, we click on Database on the left side of the dashboard.
We create a collection in our database tab by clicking on the Add Collection button. This action redirects us to a Permissions page.
At the Collection Level, we want to assign a Read Access and Write Access with a role:all ****value. You can modify these permissions to specify who has access to read or write to our database.
Next, we go to our Attributes tab to create the properties we want a document to have.
We create an integer attribute of blogCount.
Creating an Appwrite Document
After creating the attributes our document will have, we go to the Documents section of the Database.
Next, we click on the Add Document button and input a zero value to create a document in our collection. This value represents the blog reads.
On the right of our newly created document, we copy our Collection ID and Document ID, which we need to perform functions on this document.
Creating the Blog Post.
In our index.jsx
file, we create a sample blog post that we will use to measure and store our blog reads.
https://gist.github.com/Iheanacho-ai/6852e1cd43e37fe457a45ae702a109ba
Next, we elevate our blog post user interface by adding styles in the global.css
file.
https://gist.github.com/Iheanacho-ai/c35dc718ccbd62cc0339a4a18e5ee3b2
Here is how our blog post looks.
Utilizing React-intersection-observer to count blog reads
The react-intersection-observer library allows us to run a function or perform a task when a part of the website is visible in the viewport. We want to increase our blog count reads when a user has read 70 percent of the blog post with this logic.
import {useEffect, useState} from 'react';
import { useInView } from 'react-intersection-observer';
const Home = () => {
const { ref: myRef, inView } = useInView();
const [appBlogRead, setAppBlogRead] = useState(0)
const [blogCount, setBlogCount] = useState()
return (
<div className="blog">
{/* The rest of the app goes here */}
...
</div>
)
}
export default Home;
In the code block above, we do the following:
- Import the react-intersection-observer
useView
hook in ourindex.js
file - Destructure the
useView
hook to get aref
that we assign to the element we want to monitor and aninView
status to tell us if the element is present on the screen - Create two state variables, an
appBlogRead
variable to hold how many times the blog has been read per mount, which will not be more than one, and ablogCount
variable to store how many times our blog has been read in general, we will update this variable with the value from our database
Next, in our index.jsx
file, we pass our blogCount
variable to the h3
element to display our blog reads.
<h3>Blog reads: {blogCount}</h3>
Understanding that we only want a read to count when a user has gone through 70 percent of the page, we add the ref
to an element in the last quarter of our blog post. Doing this ensures that a read will not count unless that element has been on screen.
<div className="blog">
<div className="nav"></div>
<div className="blog-container">
<div className="writer">
<div className="writer-image"></div>
<div className="writer-name">Adut Elsesser</div>
</div>
<div className="blog-header">
<h3>Blog reads: {blogCount}</h3>
<h2>Mind on the road, your dilated eyes</h2>
<p>Watch the clouds float, white Ferrari</p>
</div>
<div className="blog-image"></div>
<div className="blog-story-container">
<p>
Had a good time
(Sweet 16, how was I supposed to know anything?I let you out at CentralI didn't care to state the plainKept my mouth closedWe're both so familiarWhite Ferrari, good times
</p>
<p>
Stick by me, close by me You were fine You were fine here That's just a slow body You left when I forgot to speak So I text to speech, lesser speeds Texas speed, yes Basic takes its toll on me, Eventually, eventually, yes Ah, on me eventually, eventually, yes I care for you still and I will forever That was my part of the deal, honest We got so familiar Spending each day of the year, White Ferrari Good times In this life, life In this life, life
</p>
{/* The ref */}
<p ref={myRef}>
One too many years Some tattooed eyelids on a facelift Mind over matter is magic I do magic If you think about it it'll be over in no time And that's life I'm sure we're taller in another dimension You say we're small and not worth the mention
</p>
<p>
You're tired of movin', your body's achin' We could vacay, there's places to go Clearly this isn't all that there is Can't take what's been given But we're so okay here, we're doing fine Primal and naked You dream of walls that hold us imprisoned It's just a skull, least that's what they call it And we're free to roam
</p>
</div>
</div>
</div>
Next, we use the useEffect
hook to check if the element we are monitoring is in view and if our appBlogRead
variable is less than one. If these statements are true, we want to add a read count to our appBlogRead
and blogCount
variable.
import {useEffect, useState} from 'react';
import { useInView } from 'react-intersection-observer';
import {sdk, createAnonymousSession } from '../utils.js'
const Home = () => {
const { ref: myRef, inView } = useInView();
const [appBlogRead, setAppBlogRead] = useState(0)
const [blogCount, setBlogCount] = useState()
useEffect(() => {
if(inView){
if (appBlogRead < 1) {
setAppBlogRead(appBlogRead + 1)
setBlogCount(blogCount + 1)
}else{
return
}
}
}, [inView])
return(
<div></div>
)
}
export default Home;
Storing the view count to the database
In the index.js
file, we create two functions to update and get information from our database.
const handleBlogCount = async () => {
try {
let promise = await sdk.database.listDocuments('collectionID')
setBlogCount(promise.documents[0].blogCount)
} catch (error) {
console.log(error)
}
}
const updateBlogCount = async () => {
try {
await sdk.database.updateDocument('collectionID', 'documentID', {
"blogCount": blogCount
})
handleBlogCount()
} catch (error) {
console.log(error)
}
}
useEffect(() => {
createAnonymousSession()
handleBlogCount()
}, [])
useEffect(() => {
updateBlogCount()
}, [blogCount])
In the code block above, we do the following.
- Create a
handleBlogCount
function that finds a collection using the collection ID parameter, this function updates theblogCount
variable using its response - Create a
updateBlogCount
function that finds a document using its collection ID and document ID parameters,updateBlogCount
function then updates the document using theblogCount
variable, these collection and document ID are from the document we created at the start of this tutorial - Use the React
useEffect
to fire off thecreateAnonymousSession
in ourutils.js
file and thehandleBlogCount
function once our application mounts - Runs our
updateBlogCount
function each time ourblogCount
variable is updated using ReactuseEffect
hook
We have created a robust blog post read counter in Next.js with this.
https://gist.github.com/Iheanacho-ai/d1eb08bea25f99d406e185a077a0c900
Here is how our blog post looks...
Read about 70 percent of the blog post to see the blog post read increase on the page and in the database.
Conclusion
This article discussed tracking blog reads with the react-intersection observer library, storing and retrieving these reads using an Appwrite database. As the post creates the base for an analytics implementation, update the code to restrict the blog count to only one read per user session.
Resources
Here are some resources that might be helpful:
Top comments (0)