Build your own Meme Generator with React, React Hooks and TypeScript

Alex Devero
Oct 28 · 12 min read

The best way to learn something is by doing. It works even better if it means working on something for fun. So, how about learning about React, React hooks and TypeScript by building your own meme generator? This tutorial will show you how to do it.

Table of Contents:

Briefing

Project setup

Form component

Content component

Result component

Main (index) component

  • Imports
  • Refs and states
  • Fetching the API
  • Handling the text inputs
  • Handling the image change
  • Handling the file input
  • Generating the meme image
  • Handling the “Reset” button
  • Combining fetchImage with useEffect
  • Returning all components
  • Putting it all together

Styles

Conclusion: Build your own Meme Generator…

You can find the code on my GitHub.

Briefing

This meme generator will allow you to generate png or jpg image from HTML content. This content can be anything you want. For this project, it will be a single image and two headings, positioned absolutely on the image. The first heading will be at the top of the image and the second will be at the bottom.

You will be able to add the image in two ways. First, the meme generator will fetch random image from api.imgflip.com. Don’t worry, no token or registration required. Second, you will be able to open image from your disk, using file input. To generate the png or jpg file this meme generator will use dom-to-image-more package.

About the code. This tutorial will use React hooks such as useState, useEffect and useRefs. Since you will use hooks there is no need for class components. So, you will build all components for your meme generator as functional components. You will write this meme generator in TypeScript and you will also work with interfaces and types.

Project setup

Let’s set up the files you will need to build your meme generator. You can do this very quickly by using create-react-app as your starting template. If you want, you can install this package globally on your computer, with your favorite package manager (pnpm, yarn or npm). However, this is not really necessary.

You can also create the starting template without installing anything. This can be done either with npx, instead of npm, or pnpx, instead of pnpm. These two commands will download the desired package, install it temporarily, automatically start it, and remove it after you are done. No need to fill your HDD.

One more thing, you will write this meme generator in TypeScript, a superset of JavaScript. If you want to create the starter template with create-react-app with support for TypeScript you have to include --typescript flag in the command. If you don’t want to use TypeScript in this project, omit the --typescript flag.

To the installation. For npx, use npx create-react-app react-meme-generator-ts --typescript. You can also use npm directly, npm init react-meme-generator-ts --typescript. For pnpx, it will be npx create-react-app react-meme-generator-ts --typescript. For yarn, use yarn create react-app react-meme-generator-ts --typescript.

These commands will create a starter template for your meme generator. Now, let’s also add the dom-to-image-more package. When you are done with this, you are ready to start. Your package.json will look something like this:

{
"name": "react-meme-generator-ts",
"version": "1.0.0",
"description": "Meme generator web app built with React, React hooks and TypeScript.",
"license": "MIT",
"private": false,
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"main": "src/index.tsx",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"dependencies": {
"dom-to-image-more": "2.8.0",
"react": "16.11.0",
"react-dom": "16.11.0",
"react-scripts": "3.2.0"
},
"devDependencies": {
"@types/react": "16.9.11",
"@types/react-dom": "16.9.3",
"typescript": "3.6.4"
}
}

One thing. Below is the final structure of the meme generator you are going to build. You can use this to help yourself orient in the code.

react-meme-generator-ts/
├─node_modules
├─public
│ ├─favicon.ico
│ ├─index.html
│ ├─manifest.json
│ └─robots.txt
├─src
│ ├─components
│ │ ├─content.tsx
│ │ ├─form.tsx
│ │ └─result.tsx
│ ├─styles
│ │ └─styles.css
│ ├─index.tsx
│ └─react-app-env.d.ts
├─ package.json
└─ tsconfig.json

Form component

The first component you will build will be a form. To be specific, it will actually be a div with couple of input elements and buttons. There will be two inputs, one for text at the top and one for the text on the bottom. Next, there four buttons, one for generating real png image of the meme.

Second button will change the image, load random image provided by api.imgflip.com. Third button will allow you to upload your own image from your disk. This will button will actually be file input wrapped inside label element. The fourth button will reset the image, i.e. remove the generated meme from DOM.

About the “Reset” button. The meme generator will display this button only when some meme image is generated. Otherwise, this button component will not exist in the DOM.

// Import react
import * as React from 'react'

Content component

The Content component will be very simple. There will be one wrapper div with img element to preview the meme image, and h1 for the text at the top and h2 for the text at the bottom. The wrapper div will have a ref.

You will use this ref later to make it easier to reference this div, and generate the meme from its HTML content. That’s it for the Content component.

// Import react
import * as React from 'react'

Result component

The third component you will build will be the Result component. This component will be a div that will wrap the png or jpeg image, this meme generator will create. The wrapper div will also have a ref. You will use this ref to append the newly generated meme image, and also to remove any existing when you click the “Reset” button.

// Import react
import * as React from 'react'

Main (index) component

It is time for the fourth and most important and complex component. This component will render all smaller components you’ve built so far. It will also provide them with logic and functionality. So, when you finish this component your meme generator be ready to use. Well, almost. It will need some styles. But now, the main component.

Imports

As the first thing, you will need to import react, react-dom and dom-to-image-more packages. Next, you will also need to import all components you’ve built so far, i.e. Content, Form and Result. Then, you can add import for CSS stylesheet so you can later add some CSS styles to style your meme generator.

Refs and states

At the top of the main App component, you will create refs for the content and result div elements, contentContainerRef and resultContainerRef, using useRef React hook. Next, you will add states for images fetched from API, active image, top and bottom texts and for boolean isMemeGenerated. All with React useState React hook.

function App() {
// Create refs
let contentContainerRef = React.useRef<HTMLElement | null>(null)
let resultContainerRef = React.useRef<HTMLElement | null>(null)

Fetching the API

Then will come the first method, fetchImage. This method will be async. It will use fetch method to fetch the data from api.imgflip.com endpoint. The result will be an array of images with some additional information. You will store this array in images state using the setImages React hook.

After that you will take the first image in the array and set it as active image, i.e. store it in activeImage state, using the setActiveImage.

// ...
// Fetch images from https://api.imgflip.com/get_memes
async function fetchImage() {
// Get the memes
const imgData = await fetch('https://api.imgflip.com/get_memes').then(res => res.json()).catch(err => console.error(err))
const { memes } = await imgData.data

Handling the text inputs

Second method will be handleInputChange. You will use this method to handle inputs for meme image texts, the top and bottom. You will use event.target.name and if statement to detect which text is firing the event. Then, you will change the textTop, or textBottom, state using the setTextTop, or setTextBottom, React hook.

You will use event.target.value to extract the text from the input, and pass it to the state.

// ...
// Handle input elements
function handleInputChange(event) {
if (event.target.name === 'text-top') {
// Update textTop state
setTextTop(event.target.value)
} else {
// Update textBottom state
setTextBottom(event.target.value)
}
}
// ...

Handling the image change

The third method will be handleImageChange. This method will be initiated by clicking on the “Reset” button. It will take the array of images stored in images state, generate random number, and use that number as an index to choose one random image from the array.

// ...
// Choose random images from images fetched from api.imgflip.com
function handleImageChange() {
// Choose random image
const image = images[Math.floor(Math.random() * images.length)]

Handling the file input

The fourth method will be handleImageInputChange. This method will load the file loaded via the file input and use the setActiveImage React hook to change the activeImage state to the URL created for the image file you’ve uploaded from your disk.

// ...
// Handle image upload via file input
function handleImageInputChange(event) {
// Update activeImage state
setActiveImage(window.URL.createObjectURL(event.target.files[0]))
}
// ...

Generating the meme image

The fifth method will be handleMemeGeneration. First, you will create a condition to check for any childNodes inside the result container. If there is child node, this method will remove it. Otherwise, it will proceed to generating the meme image. This will make it sure there is always only one rendered image.

The generator will generate the image in png format, using the domtoimage package and its toPng method. You can also use jpg (with toJpeg) or svg (with toSvg) formats. Next, you will pass the contentContainerRef.current as argument to the toPng method, to find the content container where you want to render the meme image.

After that, you will create new image element, use URL of the generated image as src and append this new image to DOM, using the resultContainerRef. When this is done, you will change isMemeGenerated state to true using the setIsMemeGenerated React hook. This will tell React to display the “Reset” button.

// ...
// Handle meme generation
function handleMemeGeneration() {
// Remove any existing images
if (resultContainerRef.current.childNodes.length > 0) {
resultContainerRef.current.removeChild(resultContainerRef.current.childNodes[0])
}

Handling the “Reset” button

The sixth method you will create is handleMemeReset. This method will remove existing child node inside result container, generated meme image. Then, it will set the isMemeGenerated state to false using the setIsMemeGenerated React hook. This will tell React to remove the “Reset” button.

// ...
// Handle resetting the meme generator/removing existing pictures
function handleMemeReset() {
// Remove existing child node inside result container (generated meme image)
resultContainerRef.current.removeChild(resultContainerRef.current.childNodes[0])

Combining fetchImage with useEffect

Almost the last step. You will combine useEffect React hook with fetchImage method. This will cause that when the app mounts it will automatically fetch images from the API and set the first one as active. And, you will render the App component in the DOM.

// ...
// Fetch images from https://api.imgflip.com/get_memes when app mounts
React.useEffect(() => {
// Call fetchImage method
fetchImage()
}, [])
// ...

Returning all components

The last step. Now, you will take all the components you’ve built, and imported, and add them to the main App component.

// ...
return (
<div className="App">
{/* Add Form component */}
<Form
textTop={textTop}
textBottom={textBottom}
handleImageInputChange={handleImageInputChange}
handleInputChange={handleInputChange}
handleImageChange={handleImageChange}
handleMemeGeneration={handleMemeGeneration}
handleMemeReset={handleMemeReset}
isMemeGenerated={isMemeGenerated}
/>

Putting it all together

Now, let’s put all the pieces for the App component together.

// Import react, react-dom & dom-to-image-more
import * as React from 'react'
import { render } from 'react-dom'
import domtoimage from 'dom-to-image-more'

Styles

Your meme generator is almost ready. The last thing you can do is adding some styles to make it look better.

/* Default styles */
html {
box-sizing: border-box;
font-size: 16px;
}

Conclusion: Build your own Meme Generator…

Good job! You’ve just built your own meme generator with React, React hooks and TypeScript. I hope you’ve enjoyed this tutorial and learned something new, something you can use in your future project. Next steps? Find a way to make this meme generator better. Add new features you would like it to have. Your imagination is the only limit. Have fun.

If you liked this article, then please consider subscribing.

Originally published at Alex Devero Blog.

JavaScript in Plain English

Learn the web's most important programming language.

Alex Devero

Written by

I'm Founder/CEO of DEVERO Corporation. Entrepreneur, designer, developer. My mission and MTP is to accelerate the development of humankind through technology.

JavaScript in Plain English

Learn the web's most important programming language.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade