Hooks

useClassmate

Memoize classmate components when you need to declare them inside another React component.

When to use it

useClassmate keeps a stable component reference between renders. Reach for the hook whenever you have to create a classmate component inside another component (for example because the builder depends on React state, context or hooks). Without memoization every render would create a brand-new component tree which makes React re-mount your component and can lead to hydration mismatches.

Memoize inline builders

Wrap the factory with useClassmate and return the memoized component. All props still work the same way and you can forward $-prefixed values for dynamic interpolations.

import rc, { useClassmate } from "react-classmate"

type DayStatus = "completed" | "pending"

export const WorkoutDay = ({ label, status }: { label: string; status: DayStatus }) => {
  const StyledDay = useClassmate(
    () =>
      rc.div<{ $status: DayStatus }>`
        rounded-md border p-4 text-sm font-medium transition-colors
        ${(p) => (p.$status === "completed" ? "bg-green-50 border-green-500 text-green-900" : "")}
        ${(p) => (p.$status === "pending" ? "bg-slate-50 border-slate-300 text-slate-900" : "")}
      `,
    [],
  )

  return (
    <StyledDay $status={status}>
      {label}: {status}
    </StyledDay>
  )
}

Control re-computation with dependencies

Pass a dependency array as the second argument. It behaves the same as React.useMemo: the classmate component is only re-created when one of the dependencies changes. This enables things like theme-aware builders or locale-specific layout tweaks without re-instantiating on every render.

import rc, { useClassmate } from "react-classmate"

type Tone = "primary" | "secondary"

interface InsightCardProps {
  tone: Tone
  headline: string
  description: string
}

export const InsightCard = ({ tone, headline, description }: InsightCardProps) => {
  const Card = useClassmate(
    () =>
      rc.article`
        p-6 rounded-xl border transition-all duration-200 shadow-sm
        ${tone === "primary" ? "bg-primary/5 border-primary/50" : "bg-slate-900 border-slate-800 text-white"}
      `,
    [tone],
  )

  return (
    <Card aria-label={`${tone} insight`}>
      <h3 className="text-base font-semibold mb-1">{headline}</h3>
      <p className="text-sm opacity-80">{description}</p>
    </Card>
  )
}

Keep the dependency array in sync with the values captured inside the factory. Forgetting a dependency will freeze the previous classes because the memoized component never re-computes.

Tips

  • Build the component outside of render whenever possible. Use the hook only when you truly rely on render-time data.
  • You can still extend or wrap the memoized component with rc.extend; the hook just guarantees that React sees a stable component identity.
  • Combine useClassmate with the new .logic() helper if you need derived props and memoization at the same time.