How To Build A Flip Card Component With React
The flip card is a versatile component with as many use cases as someone can imagine. A flip card can defined as a card that contains content on two sides that rotates upon user interaction. Flip cards can be used to display images, text, in eCommerce applications and in games, to many a few.
This article is a step by step tutorial for creating a basic implementation of a flip card using React. I encourage readers to code along with me, if they want, using Code Sandbox or the editor of their choice. Only a basic understanding of JavaScript and React are required, as most of the mystery behind the flip card is proper JSX structure and the knowledge of a couple lesser known CSS properties.
Check out this project on Code Sandbox
Create A React Project
To get started create a React project. While any React project will do, to follow along with this tutorial start by navigating to Code Sandbox. Create a new account (it's free), and then create a new sandbox. At this point, various template options are presented. These templates create a basic file structure and install necessary dependencies based on language and framework. Chose the React template.
This creates what amounts to a new online IDE for coding in React. This is ideal for small projects, tutorials like this, and quick prototyping. This is the starting point for this tutorial.
Install Dependencies
Using the built in dependency search tool, add the following packages.
- bootstrap - a popular CSS framework
- sass - adds useful features to CSS
- classnames - add conditional statements to classNames
These additions will be reflected in the package.json
file. Code Sandbox provides this convenient user interface as a substitute for using npm install
commands.
"dependencies": {
"bootstrap": "5.0.1",
"classnames": "2.3.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-scripts": "4.0.0",
"sass": "1.35.1"
}
Refactor Default Project Structure
A few minor changes are needed to restructure the project for this tutorial.
Rename styles.css
to styles.scss
and update the import
statement in App.js
.
Import the default Bootstrap styles
import "bootstrap/dist/css/bootstrap.min.css";
import "./styles.scss";
Create The Flip Card Component Structure
Create a new file named FlipCard.js
. Insert the following code, which is the basic structure of the flip card.
function FlipCard() {
return (
<div className="flip-card-outer">
<div className="flip-card-inner">
<div className="card front">
<div className="card-body d-flex justify-content-center align-items-center">
<p className="card-text fs-1 fw-bold">Front</p>
</div>
</div>
<div className="card back">
<div className="card-body d-flex justify-content-center align-items-center">
<p className="card-text fs-1 fw-bold">Back</p>
</div>
</div>
</div>
</div>
);
}
export default FlipCard;
The FlipCard
component structure consists of an inner and outer container. Within the inner container there are two similar code blocks, one each for the front and back of the card. A few Bootstrap classes are used to quickly scaffold the component. The additional classes will be defined in the next few steps.
Create Flip Card Data Structure
This example will illustrate three different variants of the flip card. Each variant will have a different trigger mechanism. Text is used to describe the trigger mechanism will be displayed on the front of the card, and to keep things simple, the back of each card will display the text Back
.
Add the following array to App.js
. Each object describes a card.
const cards = [
{
id: "1",
variant: "hover",
front: "Hover",
back: "Back"
},
{
id: "2",
variant: "click",
front: "Click",
back: "Back"
},
{
id: "3",
variant: "focus",
front: "Focus",
back: "Back"
}
];
Property | Description |
---|---|
id | unique identifier |
variant | describes the rotation trigger |
front | text displayed on front |
back | text displayed on back |
Add Flip Cards To The App
Add the flip cards to the App
component by importing the FlipCard
component and mapping over the cards
data array, rendering a FlipCard
for each object in the array. Some additional Bootstrap classes are used to quickly structure the layout. Additional styles will be defined in the next step.
import FlipCard from "./FlipCard";
import "bootstrap/dist/css/bootstrap.min.css";
import "./styles.scss";
const cards = [
{
id: "1",
variant: "hover",
front: "Hover",
back: "Back"
},
{
id: "2",
variant: "click",
front: "Click",
back: "Back"
},
{
id: "3",
variant: "focus",
front: "Focus",
back: "Back"
}
];
export default function App() {
return (
<div className="container">
<div className="row h-100">
<div class="col d-flex flex-column flex-md-row justify-content-around align-items-center">
{cards.map((card) => (
<FlipCard key={card.id} card={card} />
))}
</div>
</div>
</div>
);
}
Each card
object is now passed as props
to the FlipCard
component. Therefore, the data can be used in the rendering of each card to determine the text displayed on each side.
function FlipCard({ card }) {
return (
<div className="flip-card-outer">
<div className="flip-card-inner">
<div className="card front">
<div className="card-body d-flex justify-content-center align-items-center">
<p className="card-text fs-1 fw-bold">{card.front}</p>
</div>
</div>
<div className="card back">
<div className="card-body d-flex justify-content-center align-items-center">
<p className="card-text fs-1 fw-bold">{card.back}</p>
</div>
</div>
</div>
</div>
);
}
export default FlipCard;
With the basic JSX structure (and thus underlying HTML structure) in place, the result is rather uninspiring. Style rules and component logic are needed, but the inheritance of props
can be seen in the card text.
Add SCSS Styling
Styling is necessary for FlipCard
functionality. The SCSS for these components positions the front and back the card absolutely, as layers on top of each other. The back of the card is then rotated 180 degrees around the y-axis.
@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;700&display=swap');
body {
font-family: 'Ubuntu', sans-serif;
background-image: radial-gradient(farthest-corner at 40px 40px,
#f35 0%, #43e 100%);
}
.container {
height: 100vh;
}
.flip-card-outer {
width: 300px;
height: 400px;
margin: 25px 0;
&.focus-trigger:focus {
outline: 5px solid greenyellow;
outline-offset: 5px;
}
.flip-card-inner {
transform-style: preserve-3d;
transition: .5s linear .1s;
position: relative;
width: inherit;
height: inherit;
&.hover-trigger:hover {
transform: rotateY(180deg);
}
&.showBack {
transform: rotateY(180deg);
}
.card {
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
&.front {
transform: rotateY(0);
background-color: #2d2d2d;
color: #fff;
}
&.back {
transform: rotateY(180deg);
background-color: #fff;
color: #2d2d2d;
}
}
}
}
The Google Font Ubuntu has been imported and added to the body
element. A radial gradient with bold colors has been added as a background. These are aesthetic choices unrelated to flip card function.
The flip-card-outer
class is given the width
and height
properties. Obviously, these can be altered depending on use case. The layer remains static and unaffected by rotational transforms.
The flip-card-inner
class uses relative position
to give the underlying sides of the card a boundary or reference point. This is the layer that rotates 180 degrees to give the flip card its functionality. This transform is toggled by conditionally adding and removing an additional class. This logic will be coded in the next step. Other important properties include transform-style: preserve-3d
and transition: .5s .1s
. The first number of the transition is the duration and the second is the delay, both in seconds.
The card
class is positioned absolutely, and thus both the front and back are on top of each other. The back
is rotated 180 degrees around the y-axis. The color schemes of the front
and back
classes are inverted for effect.
Create The Hover Triggered Flip Card
The hover variation relies on the hover psueo-class. Since the props
data controls which FlipCard
component uses this mechanism, classnames
can be used to apply the hover-trigger
class.
import cn from "classnames";
<div
className={cn("flip-card-inner", {
"hover-trigger": card.variant === "hover"
})}
>
The classnames
library works by wrapping the className
attribute in a function. In this use case the first argument is a string that represents a permanent class declaration. In other words, the flip-card-inner
class will always be present on the element. The second argument is an object where the key is a class and the value is a conditional statement. When the conditional statement is true
the class is applied, and when it's false
the class is not applied. Therefore, the hover-trigger
class is only applied when FlipCard
is passed a variant of hover
.
Now the first card is functional.
Create The Click Triggered Flip Card
While the hover variant can be implemented with only CSS, the click variant implementation requires additional JavaScript logic. Instead of using a pseudo-class
that inherently toggles based on user behavior, the click variant relies on toggling a class itself - in this case the showBack
class. This is toggled through component state
by creating a handleClick
function to respond to the onClick
event.
The new code is highlighted below.
import { useState } from "react";
import cn from "classnames";
function FlipCard({ card }) {
const [showBack, setShowBack] = useState(false);
function handleClick() {
if (card.variant === "click") {
setShowBack(!showBack);
}
}
return (
<div
className="flip-card-outer"
onClick={handleClick}
>
<div
className={cn("flip-card-inner", {
showBack,
"hover-trigger": card.variant === "hover"
})}
>
<div className="card front">
<div className="card-body d-flex justify-content-center align-items-center">
<p className="card-text fs-1 fw-bold">{card.front}</p>
</div>
</div>
<div className="card back">
<div className="card-body d-flex justify-content-center align-items-center">
<p className="card-text fs-1 fw-bold">{card.back}</p>
</div>
</div>
</div>
</div>
);
}
export default FlipCard;
Component state
is introduced and toggled onClick
when the variant is click
. This is a good introductory example of React props
and state
creating dynamic functionality.
Create The Focus Triggered Flip Card
The focus
variant is similar to click
, but is geared towards creating an accessible component for users that rely more on their keyboards. This card also adds a bright outline when focused, adding visual context for the user. Again, this is implemented through component state
- using the same showBack
value is fine. In this case handleFocus
and handleBlur
functions are used to control state
and respond to user actions. In addition, the id
property assigned in the card data can be used as a tabIndex
- this controls how many tab presses it takes to focus the given element.
import { useState } from "react";
import cn from "classnames";
function FlipCard({ card }) {
const [showBack, setShowBack] = useState(false);
function handleClick() {
if (card.variant === "click") {
setShowBack(!showBack);
}
}
function handleFocus() {
if (card.variant === "focus") {
setShowBack(true);
}
}
function handleBlur() {
if (card.variant === "focus") {
setShowBack(false);
}
}
return (
<div
tabIndex={card.id}
className={cn("flip-card-outer", {
"focus-trigger": card.variant === "focus"
})}
onClick={handleClick}
onFocus={handleFocus}
onBlur={handleBlur}
>
<div
className={cn("flip-card-inner", {
showBack,
"hover-trigger": card.variant === "hover"
})}
>
<div className="card front">
<div className="card-body d-flex justify-content-center align-items-center">
<p className="card-text fs-1 fw-bold">{card.front}</p>
</div>
</div>
<div className="card back">
<div className="card-body d-flex justify-content-center align-items-center">
<p className="card-text fs-1 fw-bold">{card.back}</p>
</div>
</div>
</div>
</div>
);
}
export default FlipCard;
Final Thoughts
With this basic component structure in your design quiver many other applications and use cases can be explored, with the only limiting principle being the imagination. Use the fork
feature in Code Sandbox to use this example as a starting point, or port the basic style and logic into a new application. Here is a fork I created that uses the cards in an eCommerce context.