Updated on Jul 3rd, 20218 min readreactdesign

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.

Chose The React Template
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.

React Template
React Template

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

App.js
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.

FlipCard.js
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"
  }
];
PropertyDescription
idunique identifier
variantdescribes the rotation trigger
fronttext displayed on front
backtext 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.

App.js
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.

FlipCard.js
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.

Flip Cards Without Styling
Flip Cards Without Styling

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.

styles.scss
@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.

FlipCard.js
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.

Hover Flip Card Is Working
Hover Flip Card Is Working

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.

FlipCard.js
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;
Focus Flip Card Is Working
Focus Flip Card Is Working

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.

Custom Flip Cards Example
Custom Flip Cards Example
Benjamin Brooke Avatar
Benjamin Brooke

Hi, I'm Ben. I work as a full stack developer for an eCommerce company. My goal is to share knowledge through my blog and courses. In my free time I enjoy cycling and rock climbing.