How can we know if our efforts are worth the time and resources if we don't measure the most crucial digital analytics metrics? Understanding and tracking the correct metrics is critical in ensuring that marketing activities are effective.
Impression as a form of metric refers to the number of times a web content was displayed to users. This metric vastly differs from reach, which only measures the number of people that have viewed your content. It is important not to confuse an impression with an engagement.
This article demonstrates how to track image impressions using Gatsby.js and Supabase — a serverless database.
CodeSandbox and GitHub
We completed this project in CodeSandbox. Fork it to get started quickly.
The source code is on GitHub.
Prerequisites
To get the most out of this article, the following requirements apply:
Project Setup and Installation
Create a Gatsby.js app in a new folder by running the following command in the terminal:
1npm init gatsby
It will request a project title and the directory name for the project. Continue to follow the prompts to select a preferred language (JavaScript or TypeScript), CMS, styling tools, and other features. Then Gatsby.js will ask to create a Gatsby site in the directory of the project:
1Create a new Gatsby site in the folder <project-name>2 Shall we do this? <Y/n> : Yes
Navigate into the project directory and install Cloudinary React SDK, Supabase, and react-intersection-observer
dependencies.
1cd <project-name>2 npm install cloudinary-react @supabase/supabase-js react-intersection-observer
Running npm run develop
starts the project on the local development server at https://localhost:8000 in our browser.
Setting up a Supabase Project
What is Supabase?
Supabase is an open-source Firebase alternative made up of tools that help developers build projects faster by handling backend functions.
Behind the scenes, Supabase uses the PostgreSQL database and it is regarded widely as one of the best tools based on Postgres - a highly scalable relational database. The platform develops a REST API from the database's tables and columns. Its autogenerated APIs include built-in features like filtering and sorting.
To get started, create an account in Supabase here. We'll also need a GitHub account; click here to create one.
After logging in, we’ll be redirected to the Supabase dashboard, as shown below, where we’ll create a new Supabase project for the demo application.
Set a name and password for the new project and click on the “New project" button to create the project as shown below.
As the project is being built, we’ll create the database for the image impressions by clicking on the database icon shown on the sidebar. After that, click on the “New” button at the top right of the screen to create a table for the database.
We created multiple columns for the database table in the above image. The created columns represent the following:
id
- key value to access the databasename
- name of the databasecreated_at
- time stamp for creating the databaseviews
- number of image impressions
After creating these columns, we’ll access the Table editor by clicking the Table icon shown on the sidebar to add a default value for our table. Click on “insert row” and add the default values below.
Navigate to the SQL editor and create a query tab to add a stored procedure to our database. A stored procedure allows us to add or extend functionality to the database.
Add the code snippet below to the new query to create the stored procedure for our database:
1create function increment (row_id int)2 returns void as3 $$4 update pages5 set views = views + 16 where id = row_id;7 $$8 language sql volatile;
Let's break down the query above:
- Creates an increment function, with the row
id
as an argument set views = views + 1
updates/increases the image impressions value calledviews
in the database table by 1
Then, click the “RUN” button to create the function. Later we’ll utilize the function to update the image impressions.
Setting up an Image in Cloudinary
Cloudinary is a cloud-based picture and video management service that includes uploads, storage, manipulations, optimizations, and distribution. It also enables developers to include video players in their apps that properly handle video events.
After successfully creating an account, Cloudinary will redirect us to our account's dashboard page, where we can upload the demo image.
Click on the “Upload” button as shown above and select the image file to be uploaded.
Implementing the Cloudinary Image
In the index.js
file, we’ll import the required components from Cloudinary React SDK and integrate the Cloudinary image, as shown below:
1//pages/components/index.js23 import React from "react";4 import { Image, CloudinaryContext } from "cloudinary-react";56 const IndexPage = () => {7 return(8 <CloudinaryContext cloud_name="OUR-CLOUD-NAME">9 <div className="image-container">10 <Image11 publicId="OUR-IMAGE-NAME"12 width="300px"13 height="400px"14 />15 </div>16 </CloudinaryContext>17 )};1819 export default IndexPage;
In the code above, we do the following:
- Apply the Cloud name from our Cloudinary account details on the dashboard page
- Include the name of the image from Cloudinary in the Image component
- Give the image a width of 300px and a height of 400px
Next, we’ll loop through an array of dummy text to make the app look better:
1const IndexPage = () => {2 return(3 <main className="container">4 <title>Track Image impressions in Gatsby.js with Supabase</title>5 <h1>tracking image impressions in gatsby.js with supabase</h1>6 {Array.from(Array(5).keys()).map((i) => (7 <p key={i}>8 Irure pariatur velit est anim ipsum anim aliquip officia velit9 consectetur. Duis sint ut consectetur ea anim. Sit proident culpa10 velit officia do incididunt Lorem in deserunt non adipisicing occaecat11 magna. Occaecat occaecat esse excepteur consequat occaecat cupidatat12 aliquip labore esse ad ea. Laboris id excepteur nisi voluptate sunt13 anim commodo amet reprehenderit.14 </p>15 ))}1617 // ... { Cloudinary Image }1819 {Array.from(Array(8).keys()).map((i) => (20 <p key={i}>21 Irure pariatur velit est anim ipsum anim aliquip officia velit22 consectetur. Duis sint ut consectetur ea anim. Sit proident culpa23 velit officia do incididunt Lorem in deserunt non adipisicing occaecat24 magna. Occaecat occaecat esse excepteur consequat occaecat cupidatat25 aliquip labore esse ad ea. Laboris id excepteur nisi voluptate sunt26 anim commodo amet reprehenderit.27 </p>28 ))}29 </main>30 );31 };32 export default IndexPage;
After configuring the dummy text, the demo application will look like this:
Integrating react-intersection-observer
react-intersection-observer
is the React implementation of the Intersection Observer API that indicates when an element enters or leaves the viewport. It contains a hook, renders props, and plain children implementation.
It provides a useInView
hook, which makes it easy to monitor the state of our components. We'll use the custom InView
component, which will be called whenever the state changes, with a ref
that should be assigned to the element root. The InView
component uses a default state of true or false, so if the image enters the viewport, its state is true, and if it leaves the viewport, its state is false.
1//index.js23import React from "react";4import { Image, CloudinaryContext } from "cloudinary-react";56const IndexPage = () => {7return(89//array of dummy text1011<CloudinaryContext cloud_name="OUR-CLOUD-NAME">12 <InView>13 {({ ref }) => (14 <div className="image-container" ref={ref}>15 <Image16 publicId="OUR-IMAGE-NAME"17 width="300px"18 height="400px"19 />20 </div>21 )}22 </InView>23 </CloudinaryContext>2425// array of dummy text2627)};28export default IndexPage;
Updating the Image Impressions
We’ll need to link our Supabase API URL and API Key to the demo application. First off, head to Supabase’s settings, then go to the “API” section, where we can access our Supabase project’s API URL and API Key as shown below:
Then, create a .env
file in the app’s root directory to store the API URL and API Key:
1#.env2 SUPABASE_URL= OUR_PUBLIC_SUPABASE_URL3 SUPABASE_KEY= OUR_PUBLIC_SUPABASE_KEY
Next, we’ll add the API URL and API Key to the index.js
file:
1//pages/components/index.js2 import { createClient } from "@supabase/supabase-js";34 const IndexPage = () => {5 const supabase = createClient(6 process.env.SUPABASE_URL,7 process.env.SUPABASE_KEY8 );9 return (10 // Cloudinary Image11 )};
To update the Supabase database, we’ll create the function, endFunction
, which is triggered anytime the state of the InView
component changes. The function uses a Supabase Remote Procedure Call (RPC) to increase the number of impressions at Supabase’s database by 1:
1//index.js2 import React, { useState } from "react";3 import { Image, CloudinaryContext } from "cloudinary-react";45 const IndexPage = () => {6 const supabase = createClient(7 process.env.SUPABASE_URL,8 process.env.SUPABASE_KEY9 );10 const endFunction = async (inView) => {11 if (inView) {12 const { data, error } = await supabase.rpc("increment", { row_id: 1 });13 }14 };15 return (16 <CloudinaryContext cloud_name="OUR-CLOUD-NAME">17 <InView onChange={(inView) => endFunction(inView)}>1819 // .....2021 </InView>22 </CloudinaryContext>23 )};
After testing the demo application, it should look like this:
Conclusion
This article discussed what Supabase is, the advantages of implementing it in applications, and, more importantly, how to integrate Supabase into web applications.