No Time Dad

A blog about web development written by a busy dad

Tailwind Card Component With React

Creating a tailwind card component is fun. So much fun that I’ve made a few of them recently. I thought it might be fun to explore a reusable react tailwind card component that just contains the scaffolding and leaves the content up to the user. Utilizing props to determine what each section of the card will contain.

Header
Content
Footer
import React from 'react';

export const Card = ({ header, content, footer}) => (
  <div className="shadow-md rounded-md bg-white">
    <div className="p-2 border-b">{header}</div>
    <div className="p-2">{content}</div>
    <div className="p-2 border-t">{footer}</div>
  </div>
);

Boring, I know. But, it’s not meant to be used as-is like this. Instead, I’ll try passing components instead of strings as props. This’ll start bringing the card to life.

Card HeaderInfo
Content
Footer
<Card header={<Header />} content="Content" footer="Footer" />
export const Header = () => (
  <div className="flex justify-between">
    <span className="text-3xl">Card Header</span>
    <a href="#info" class="bg-indigo-500 px-4 py-1 text-white text-center font-extrabold rounded-full hover:bg-indigo-600">Info</a>
  </div>
);

Above I’ve created a slightly improved header that increases the header text size and adds an info button. The width of the card should be determined by the content section. This makes the card easier to manage, and the user probably only cares about the content. So it’s a good idea to focus on that section and let it dictate how the card looks.

Card HeaderInfo
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.
Footer
<Card header={<Header />} content={<Content />} footer={<Footer />} />
export const Content = () => (
  <div className="w-96 py-2">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.</div>
);

export const Footer = () => (
  <div className="flex justify-between opacity-70 items-center">
    <span>Footer</span>
    <div>
      <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
        <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
      </svg>
    </div>
  </div>
);

The Content component in my example here is mostly just stub text. The footer is pretty basic as well, just containing some text and an icon. But, the point is that it could contain any element because the props are flexible. That’s what makes this tailwind card component so flexible. You can just slot in whatever elements you’d like to fill out the template.

Full code:

// package.json
...
"react": "^17.0.2",
"tailwindcss": "^2.1.2"
...
import React from 'react';

export const Card = ({ header, content, footer }) => (
  <div className="shadow-md rounded-md bg-white">
    <div className="p-2 border-b">{header}</div>
    <div className="p-2">{content}</div>
    <div className="p-2 border-t">{footer}</div>
  </div>
);

export const Header = () => (
  <div className="flex justify-between">
    <span className="text-3xl">Card Header</span>
    <a href="#info" class="bg-indigo-500 px-4 py-1 text-white text-center font-extrabold rounded-full hover:bg-indigo-600">Info</a>
  </div>
);

export const Content = () => (
  <div className="w-96 py-2">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.</div>
);

export const Footer = () => (
  <div className="flex justify-between opacity-70 items-center">
    <span>Footer</span>
    <div>
      <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
        <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
      </svg>
    </div>
  </div>
);