DEV Community

Cover image for TW Classed - Make reusable Tailwind components
Sanna Jammeh
Sanna Jammeh

Posted on

TW Classed - Make reusable Tailwind components

Earlier last month I wrote an article titled "Im merging css-in-js and Tailwind". A lot has happened since then, I've published the v 1.0.0 version of my library and received a ton of great feedback from the community. We are now at v1.3.0! It's time to give this library a proper announcement.

Introducing TW Classed V1

Tailwind CSS and CSS-in-JS, the best of both worlds. Great DX and user experience right out of the gate.

Full Documentation

Here is what it solves:

  1. Simple encapsulation of class names
  2. Toggle-able class names via props
  3. Extending existing components
  4. Fully typesafe all the way down to the HTML definitions

How does it work?

If you've used Stitches or Styled Components this will feel very familiar. TW Class does not inject any styles, it servers as a class toggler and component generator.

  1. call classed and define what element you want to render
  2. Add your class names
  3. Add any variants (Stitches developers will feel right at home)
  4. Render it!

Below are some examples of what can be done:

A simple component with color prop

import { classed } from "@tw-classed/react";
// classed("button", ...classes) is also possible
const Button = classed.button("px-4 py-2", {
  variants: {
    color: {
      primary: "bg-blue-500 text-white",
      secondary: "bg-gray-500 text-white",
    },
  },
});

() => <Button color="primary">Primary</Button>
() => <Button color="secondary">Secondary</Button>
Enter fullscreen mode Exit fullscreen mode

Multi argument support

TW Classed supports an infinite number of arguments. You can pass it any combination of strings (will be set as classes), variants (classes toggled by props) or even other classed components (current component will inherit all classes and props).

const Grid = classed.div(
  "grid gap-4", // Mobile first
  "md:grid-cols-2 md:gap-6", // Tablet
  "lg:grid-cols-3 lg:gap-8", // Desktop
  "xl:grid-cols-4" // Large desktop
);

const BlueGrid = classed(Grid, "bg-blue-500");

// Doesn't render Grid but grabs all classes and variants and injects into classed.section
const Section = classed.section("bg-blue-500", Grid)
Enter fullscreen mode Exit fullscreen mode

Extending other components

// Link.tsx
import Link from "next/link";

const ClassedLink = classed(Link, "text-blue-500 hover:text-blue-700");

// In your App
() => <ClassedLink href="/docs">Go to docs</ClassedLink>;

// Or by overriding the rendered element
const Anchor = classed("a", "text-blue-500 hover:text-blue-700");

// In your App
() => <Anchor as={Link} href="/docs">Go to docs</Anchor>;
Enter fullscreen mode Exit fullscreen mode

All this and more features like defaultVariants, compoundVariants, advanced class name merging, Tailwind Extension support and a framework agnostic library is available in the Documentation

The why?

Creating design systems with plain React components and Tailwind can be quite tricky. CSS in JS solved this perfectly, but many of us enjoy working with Tailwind.

Creating React components in a typesafe design system requires:

  • Creating TypeScript props
  • Extending the underlying element's props
  • Add Types for props controlling class names
  • Manually mapping the class names to certain prop values (which may or may not change...)
  • Adding forwardRef

I therefore made sure all this is handled out of the box in tw-classed. It's as close to a CSS in JS library as possible, but with pure classes.

Footnote

Thank you for reading this article! I have received great support from the community and tw-classed wouldn't have been as good as it is today without you guys! I always welcome any kind of feedback which can be sent here:

Github
Discord server

Top comments (2)

Collapse
 
bonitoflakes profile image
Rishab Khivsara

Quick question: Does this create runtime overhead?

Collapse
 
sannajammeh profile image
Sanna Jammeh

Very little. Its equally fast as CVA and 2x faster than stitches (fastest css in JS lib). But there is always a slight perf hit when we’re dealing with loops