Rocketmeme - A Meme Library and Editor for creating and sharing memes
What is Rocketmeme
Rocketmeme is a simple and easy-to-use library and meme editor built with React and Hasura GraphQL for creating, sharing, viewing, and downloading memes on the fly. Building Rocketmeme has been an amazing experience filled with learning new things and fixing bugs. This project was built by me and my good friend Spiff Jekey Green: an amazing backend developer who tirelessly contributed to bringing this idea to fruition.
Inspiration
Memes have become a part of our everyday interaction with one another, you see them shared in videos, social media, articles, and websites like daily.dev.
Statistics have shown that humor is a great way to draw people's attention or even cause interaction between people, and that is what memes are all about. Don't believe us? Check out Elon Musk on Twitter. :)
Nah, he's kidding. ๐
UI & Style Guide
Now we got the inspiration and knew what we plan on building, the next step was designing what it would look like. For the UI, we drew inspiration from Unsplash and for the style guide, I did a google search for ideas and saw a cool template on Figma. I quickly stole, sorry forked the template, and updated the styles accordingly. :)
Project Setup
We used GitHub automated Kanban board to list out the major features for Rocketmeme and assigned a priority label to each issue to mark their level of importance. A major feature like setting up Rocketmeme API with Hasura GraphQL will take top priority and thus label would be marked: Priority: High
Tech Stack
Rocketmeme is completely built with React for the frontend and Hasura for the backend. Below is a small list of libraries and tech stacks used to make this app what it is.
Features
This app started as a simple meme editor but has gradually morphed into a library of memes with an API. Here are some of the core features of Rocketmeme:
1. Meme search
Rocketmeme features a simple search bar on the homepage that allows users to browse through memes based on different categories like JavaScript, NFT, stackoverflow, ETC. At the time of writing this article, we've only added a few memes to the API so the results might be few for now.
The meme search is powered by Hasura and fully paginated to avoid too many requests at once. By default, the search is set to search by category but it can be configured to search by id, title, and more.
const getPaginatedMemeSearch = gql`
query getPaginatedMemeSearch($searchTerm: String!) {
memes(limit: 10, offset: 0, where: { category: { _ilike: $searchTerm } }) {
id
downloads
likes
views
image_link
category
title
}
}
`;
2. Meme Editor
The meme editor is powered by Interact JS, a powerful JavaScript drag & drop multi-touch library for creating editable fields with text and inputs. With the editor, users can edit a meme using a collection of high-quality images.
To create a meme, select from the meme templates on the top, which are lazy-loaded using react-lazy-loader to save the user's data and increase load time. After selecting the meme, click the "add text" button to start making the meme. The text can be resized or repositioned around the canvas.
You can also add icons and images from the image list or upload your preferred image from your device. To do this, click the "Add image" button, which will pop up a simple modal of icons, select your preferred image, and once added, you can resize or reposition the image around the canvas.
3. Meme API
https://rocketmeme-user.hasura.app/v1/graphql
With Hasura, we were able to create a serverless API using Hasura cloud. The API exists for two types of requests: The meme template and the uploaded memes which are handled by Hasura. If you're interested, you can start exploring the API docs to get an idea of everything possible with the rocketmeme API.๐
Here is an example snippet of the API exported by Hasura - if you plan on using it, you should consider tailoring it to your service.
async function fetchGraphQL(operationsDoc, operationName, variables) {
const result = await fetch(
"undefined",
{
method: "POST",
body: JSON.stringify({
query: operationsDoc,
variables: variables,
operationName: operationName
})
}
);
return await result.json();
}
const operationsDoc = `
query MyQuery {
meme_templates(order_by: {id: asc, image_link: asc, title: asc}) {
id
image_link
title
}
}
`;
function fetchMyQuery() {
return fetchGraphQL(
operationsDoc,
"MyQuery",
{}
);
}
async function startFetchMyQuery() {
const { errors, data } = await fetchMyQuery();
if (errors) {
// handle those errors like a pro
console.error(errors);
}
// do something great with this precious data
console.log(data);
}
startFetchMyQuery();
A typical response from the API looks something like this.
{
"data": {
"meme_templates": [
{
"id": 13,
"image_link": "https://ik.imagekit.io/eke/meme-template/6a9d61_Ymb7HBcvJ.png?ik-sdk-version=javascript-1.4.3&updatedAt=1648578651816",
"title": "Will Punching Chris"
},
{
"id": 4,
"image_link": "https://ik.imagekit.io/eke/meme-template/temp1_0nO6rB9BNK.png?ik-sdk-version=javascript-1.4.3&updatedAt=1648026808159",
"title": "X, X Everywhere"
}
]
}
}
4. Login
Creating an account in Rocketmeme can give you features like saving your edited memes, connecting your social accounts to easily share memes on your favorite social platform, upvoting or downvoting a meme, and more. For now, the login feature is disabled until we set up the user endpoint in Hasura, which will allow users that are logged in to upload memes they created. :)
Challenges
I'll say this has been the most challenging project I've worked on, so far and I wouldn't have been able to build this without the help of my good friend Spiff. I learned a lot by collaborating with him and this will not be our last project together.
Grid gallery
Rocketmeme features an awesome-looking grid layout for the uploaded memes on the homepage. This is being controlled by the column-count property. The CSS property I discovered was not done by any library because we wanted the application to be fast and most of the grid gallery libraries available were either on vanilla JS or jQuery. Building this gallery was very tough as some contents overlapped on each other when we first tested it. What we did was calculate the total width of the container and divide it into 3 parts where each part will occupy an image. We used the closest whole number 33% for each. This equally divided the containers and the size was perfect.
Dynamic image resizing
It is no surprise that I would require a way to dynamically serve Rocketmeme images based on different devices. This is to ensure images remain at the smallest size and with the best quality possible. I struggled with a way to do this until I discovered Don't serve unoptimized images by James Q Quick. In this tutorial, he utilized a service called ImageKit for optimizing images dynamically.
We used ImageKit to host all the images for the API. Copied the image URL and added it to our database in Hasura Cloud.
Additional Features and Summary
These are some of the features we weren't able to implement before launching Rocketmeme. Though we are currently working on them so keep an eye out for the next release. ๐
- User Authentication
- Meme uploads by users.
- Users upvote and downvote memes.
- Meme categories page.
Last Words
Thank you so much ๐คฉ Hashnode and Hasura for putting together this hackathon and to you reading this right now, I say a big thank you for taking out your time to go through this article. I genuinely appreciate it.
Rocketmeme is completely open-source so anyone willing to help collaborate in adding new features is most welcome to make a pull request, not just with code but any contribution that can make the project more suitable would be appreciated as well. And with that, I wrap up this article and hope to get your feedback for Rocketmeme in the comments below or the feedback section in rocketmeme.
Links
Subscribe to my newsletter
Read articles from Victor Eke directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Victor Eke
Victor Eke
My expertise lies in building interactive web applications on the client side. Primarily working with technologies like JavaScript, Next.js, TypeScript and Python. I strongly believe in continuous learning and improving myself, so I try my best to learn in any situation possible, unfavorable or not. Beyond learning, I enjoy writing technical articles and creating projects that both inspire and benefit fellow developers.