In this post, I talk about how to create an Image Gallery with Remix, HarperDB, ImageKit and Vercel. We learn how to create dynamic routes in Remix, process form submissions on the server, handle client side image uploads, sync data with HarperDB and search conditionally using HarperDB NoSQL Operations.
- Remix (Frontend and Serverless Backend)
- HarperDB (Database and Caching)
- ImageKit (Client Side Image Uploads)
- Tailwind CSS (Styling)
- Vercel (Deployment)
- Node.js 18
- A ImageKit account (for client side image uploads)
- A HarperDB account (for setting up NoSQL database)
- A Vercel account (for deploying your website)
Setting up the project
To set up, just clone the app repo and follow this tutorial to learn everything that's in it. To fork the project, run:
Once you have cloned the repo, you are going to create a .env file. You are going to add the values we obtain from the steps below.
Setting up HarperDB
Let’s start by creating our database instance. Sign in to Harper Studio and click on Create New HarperDB Cloud Instance.
Fill in the database instance information, like here, we’ve added chatbot as the instance name with a username and password.
Go with the default instance setup for RAM and Storage Size while optimizing the Instance Region to be as close to your serverless functions region in Vercel.
After some time, you’d see the instance (here, image-gallery) ready to have databases and it’s tables. The dashboard would look something like as below:
Let’s start by creating a database (here, list) inside which we’ll spin our storage table, make sure to click the check icon to successfully spin up the database.
Let’s start by creating a table (here, collection) with a hashing key (here, id) which will be the named primary key of the table. Make sure to click the check icon to successfully spin up the table.
- Open app/lib/harper.ts and update the database and table values per the names given above.
- Click on config at the top right corner in the dashboard, and:
- Copy the Instance URL and save it as HARPER_DB_URL in your .env file
- Copy the Instance API Auth Header and save it as HARPER_AUTH_TOKEN in your .env file
Awesome, you’re good to go. This is how the data looks for a record of each image.
Configuring NoSQL CRUD helpers for HarperDB for Vercel Edge and Middleware Compatibility
To interact with the HarperDB database, we’ll use NoSQL HarperDB REST APIs called over fetch. This approach will help us opt out of any specific runtime requirements, and keep things simple and ready to deploy to Vercel Edge and Middleware.
In the code below, we’ve defined the CRUD helpers, namely insert, update, deleteRecords, searchByValue and searchByConditions for respective actions.
Handling Client Side Image Uploads with ImageKit
I was always intrigued by how very large images were uploaded with forms on the server. Then reality hit me, they are not. Large files are usually uploaded from the client side directly to the Storage, and the asset URL obtained is then used in form processing on the server.
Using ImageKit React, handling client side image uploads to ImageKit storage is a cakewalk. Just initialize the ImageKit Context (IKContext) with authenticator endpoint (here, /imagekit) and url endpoint to your particular ImageKit Instance URL (here, https://ik.imagekit.io/vjeqenuhn). Programmatically, click the hidden IKUpload component which handles the callback process of uploading the asset directly to ImageKit storage and you’re done!
To setup ImageKit Authenticator Endpoint for client side uploads, we’ll use the imagekit package. As the authenticator configured in the IKContext is pointing to /imagekit as the endpoint, we’re gonna create app/routes/imagekit.tsx to serve the requests.
First, we initialise the ImageKit instance and then use the baked-in getAuthenticationParameters function to serve the required JSON response while authentication is performed for initiating client side uploads.
Handling Form Submissions in Remix
With Remix, Route Actions are the way to perform data mutations (such as processing form POST request) with web standard & semantics and loaded with DX of modern frameworks. Here’s how we’ve enabled form actions with Remix in app/routes/index.tsx 👇🏻
Using ImageKit Image Transformations to create Blur Images
To create blur images from the image uploaded to ImageKit, we’re gonna use the ImageKit Image Transformations. By appending ?tr=bl-50 to the original image URL, ImageKit takes care of creating and responding with 50% blurred image. Further, we store the blurred image’s buffer as base64 string to be synced into the HarperDB database.
For creating images that are near to ideal for User Experience, we create the blur-up effect. By blurring-up an image, I’m referring to the images where the image is instantly visible but is blurred and one can’t see the content inside it, while the original (whole) image loads in parallel.
To create such blur-up effects, we set the base64 string of blur version of the image as the background, and image source as the original image’s remote URL.
Insert Image Record(s) in the HarperDB Database
Great! Now, we’ve obtained all the attributes related to the photograph, including the blurred version of the photograph and photographer’s details. The last step in syncing this information to database is to insert it into the HarperDB database using Insert NoSQL Operation.
Implementing Images Wide Search with HarperDB Search By Conditions Operation
For fetching all the image records, in the Route loader function, we search by value (HarperDB NoSQL operation) matching anything (denoted by *) from the records in HarperDB. This helps us show all the images as soon as someone opens up the /pics page.
To implement the search functionality, we make use of Remix Form Actions and HarperDB Search By Conditions NoSQL Operation. Via this operation, we're able to create our matching conditions and not only look for exact values in the records.
In our case, as one submits something into the search bar, the Route action is invoked with the request containing the search query. Using the searchByConditions helper, which basically looks if the search string is present as the substring in each attribute’s value in each record.
Creating Dynamic Routes in Remix
To create a page dynamically for each image, we're gonna use Remix Dynamic Routes. To create a dynamic route, we use the $ symbol in the route’s filename.
Here, pics_.$id.tsx specify a dynamic route where each part of the URL for e.g. for /pics/1, /pics/2 or /pics/anything captures the last segment into id param.
Using the dynamic param (here, id) we fetch the record from HarperDB containing slug attribute equal to id value via loader function.
Using the data fetched from HarperDB, we use our Blur Up Image component to lazy load the photographer's image while eagerly load the photograph uploaded.
Whew! All done, let's deploy our Remix app to Vercel 👇
Deploy to Vercel
The repository is ready to deploy to Vercel. Follow the steps below to deploy seamlessly with Vercel 👇🏻
- Create a GitHub Repository with the app code
- Create a New Project in Vercel Dashboard
- Link the created GitHub Repository as your new project
- Scroll down and update the Environment Variables from the .env locally
- Deploy! 🚀