Skip to main content

Quick start

Unwind aims to be framework-agnostic. You can use React, Vue, Solid, Svelte or whatever you like, however…

  • In this guide, we will cover how to unwind the className prop of a typical Button component built with React.
  • Our goal is to enhance the className prop with @unwind/class-name so that it changes color when active state is set to true.

Prerequisites

1. Combining class selectors
We will use Array.join() to get resulting className as string. You could be also using clsx, classname, template literals... you name it.

2. Example component
We will use a Button component bellow styled with Tailwind classes. Tailwind is already running in your browser to play with and provides style on demand while you edit the live previews. CSS stylesheet in your case might be coming from elsewhere but it should not matter for the sake of this guide.

tip

Go ahead, play with the code. Unless you remove the App declaration, the live preview should work.

Button.tsx
Live Editor
Result
Loading...

1. Installation

Add package to package.json

pnpm add @unwind/class-name

2. Rewriting the className prop

  1. Import from @unwind/class-name
  2. Replace base component styles with defineClassName()
  3. Replace className prop type with ClassNameProp<...>
  4. Replace Array.join() with resolveClassName()
Button.tsx (before)
import React, { PropsWithChildren } from 'react'const buttonClassName = [  'bg-gray-50 hover:bg-white active:bg-gray-200',  'border border-gray-300 rounded',  'text-black font-bold',  'py-2 px-4',]type ButtonProps = PropsWithChildren<{  className?: string}>const Button = ({ children, className }: ButtonProps) => (  <button    className={[      ...buttonClassName,      className,    ].join(' ')}  >    {children}  </button>)
Button.tsx (after)
import type { ClassNameProp } from '@unwind/class-name'import { defineClassName, resolveClassName } from '@unwind/class-name'import React, { PropsWithChildren } from 'react'const buttonClassName = defineClassName([  'bg-gray-50 hover:bg-white active:bg-gray-200',  'border border-gray-300 rounded',  'text-black font-bold',  'py-2 px-4',])type ButtonProps = PropsWithChildren<{  className?: ClassNameProp<typeof buttonClassName>}>const Button = ({ children, className }: ButtonProps) => (  <button    className={resolveClassName(      {},      buttonClassName,      className,    )}  >    {children}  </button>)

3. Add the active state

Unwind lets you define how components should render className according to state with a callback. Lets add toggle state change to the Button.

Button.tsx (before)
import type { ClassNameProp } from '@unwind/class-name'import { defineClassName, resolveClassName } from '@unwind/class-name'import React, { PropsWithChildren } from 'react'const buttonClassName = defineClassName([  'bg-gray-50 hover:bg-white active:bg-gray-200',  'border border-gray-300 rounded',  'text-black font-bold',  'py-2 px-4',])type ButtonProps = PropsWithChildren<{  className?: ClassNameProp<typeof buttonClassName>}>const Button = ({ children, className }: ButtonProps) => {  return (    <button      className={resolveClassName(        {},        buttonClassName,        className,      )}    >      {children}    </button>  )}
Button.tsx (after)
import type { ClassNameProp } from '@unwind/class-name'import { defineClassName, resolveClassName } from '@unwind/class-name'import React, { PropsWithChildren, useCallback, useState } from 'react'const buttonClassName = defineClassName(({ active }: { active: boolean }, previous) => [  ...previous,  active    ? 'bg-green-500 shadow-inner active:bg-green-600'    : 'bg-gray-50 hover:bg-white active:bg-gray-200',  active ? 'border-green-600' : 'border-gray-300', 'border rounded',  active ? 'text-white' : 'text-black', 'font-bold',  'py-2 px-4',])type ButtonProps = PropsWithChildren<{  className?: ClassNameProp<typeof buttonClassName>}>const Button = ({ children, className }: ButtonProps) => {  const [active, setActive] = useState(false)  return (    <button      className={resolveClassName(        { active },        buttonClassName,        className,      )}      onClick={useCallback(() => { setActive(state => !state) }, [])}    >      {children}    </button>  )}

4. The final code

We updated className prop to accept various types of values:

Live Editor
Result
Loading...