Get started

Utils

Reuse, Share & Extend

Extend Components

The extend function allows extending upon almost any react-component with our classmate syntax.

Basic Syntax

We pass a input component and extend it with our classmate syntax. The extend function returns a new component.

// this could be almost any component that accepts a className prop
const MyInput = ({ ...props }: InputHTMLAttributes<HTMLInputElement>) => <input {...props} />

const StyledInput = rc.extend(MyInput)<{ $trigger?: boolean }>`
  bg-white
  border-1
  ${(p) => (p.$trigger ? "border-error" : "border-gray")}
`

Extend as often as you wish:

const ExtendedStyledInput = rc.extend(StyledInput)<{ $someBool?: boolean }>`
  custom-class
  ${(p) => (p.$someBool ? "shadow" : "")}
  ${(p) => (p.type === "text" ? "text-lg" : "")}
  ${(p) => (p.$trigger ? "text-red" : "")}
`
Unsure what we do in the interpolation above? Head back to the Base documentation page

Implementation

<StyledInput type="text" $trigger />
// renders:
// <input type="text" class="bg-white border-1 border-error" />

<ExtendedInput type="text" $trigger $someBool />
// renders:
// <input type="text" class="bg-white border-1 border-error custom-class shadow text-lg text-red" />

When to use rc.extend?

There are two different scenarios where extending components is helpful:

1. Reduce redundancy

When you wanna extend a "base"-component, in which you desired to set less specific styling classnames (e.g. outer margins, typography, etc.) rc.extend could be your pick. For example these are some elements which are re-used all over this documentation:

// ...
import Notebox from "#components/common/Notebox"

export const Section = rc.extend(Notebox)`
  mb-8
`
export const SectionHeadline = rc.extend(H3Headline)`
  mb-4
`
export const SectionInnerHeadline = rc.extend(H4Headline)`
  mt-4
`
// .... 

2. Extend classnames of a third-party library component

import { ArrowBigDown } from "lucide-react"
import rc from "react-classmate"

const StyledLucideArrow = rc.extend(ArrowBigDown)`
  md:-right-4.5
  right-1
  slide-in-r-20
`

// ts: we can pass only props which are accessible on a `lucid-react` Component
export default () => <StyledLucideArrow stroke="3" />

When to not use

A classic example where you find yourself extending the same component multiple times to assign almost similar classnames:

// probably unused :/
const GradientBase = rc.div`
  absolute -mt-100 left-0 w-full
  h-100 
  bg-gradient-to-t
  pointer-events-none
`

const VariantsGradient = rc.extend(GradientBase)`
  from-primarySuperLight 
  top-0
`
const ThanksGradient = rc.extend(GradientBase)`
 from-primaryLight/40
 bottom-0
`
const FooterGradient = rc.extend(GradientBase)`
 from-light
 bottom-0
`

Why is this bad practice?

  • Cluttered, redundant code
  • Base component is likely going to be unused

How to fix?

In the example above it's very likely that we will not implement the GradientBase in the jsx later and might want to consider creating a variant out of our blueprint from above. Here is an example how we could do it:

type GradientType = "variants" | "thanks" | "footer"

const SectionGradient = rc.div.variants<{ $type: GradientType }>({
  base: `
    absolute -mt-100 left-0 w-full
    h-100 
    bg-gradient-to-t
    pointer-events-none
  `,
  variants: {
    $type: {
      variants: "from-primarySuperLight top-0",
      thanks: "from-primaryLight/40 bottom-0",
      footer: "from-light",
    }
  }
})