Builder helpers
Colocate pure setup logic with your classmate component and expose the result to the template, variants and DOM output.
Call .logic() before the template literal to run arbitrary JavaScript on every render. The function receives the current props and can return additional props that should be merged back in. Anything prefixed with $ is stripped from the DOM but stays available for interpolations and variants.
import rc from "react-classmate"
type DayStatus = "completed" | "skipped" | "pending"
interface WorkoutProps {
workouts: number
completed: number
skipped: number
$status?: DayStatus
}
export const WorkoutDay = rc.div
.logic<WorkoutProps>((props) => {
if (props.completed === props.workouts) {
return { $status: "completed" }
}
if (props.skipped > 0) {
return { $status: "skipped" }
}
return { $status: "pending" }
})
.variants<WorkoutProps, { $status: DayStatus }>({
base: "rounded-md border p-4 transition-colors",
variants: {
$status: {
completed: "border-green-500 bg-green-50",
skipped: "border-orange-400 bg-orange-50",
pending: "border-slate-300 bg-white",
},
},
defaultVariants: {
$status: "pending",
},
})
Logic handlers must stay pure. They cannot use hooks or render JSX. Think of them as a small “header” that derives data for the actual component.
You can chain as many logic handlers as you need. Later handlers receive the props returned by previous ones which makes it trivial to set derived variant props, data-* attributes or accessibility metadata in one place.
import rc from "react-classmate"
interface NotificationProps {
events: { type: "error" | "info" }[]
$severity?: "idle" | "error"
$hasErrors?: boolean
}
export const NotificationBadge = rc.button
.logic<NotificationProps>((props) => {
const hasErrors = props.events.some((event) => event.type === "error")
return {
$hasErrors: hasErrors,
$severity: hasErrors ? "error" : "idle",
}
})
.logic<NotificationProps>((props) => ({
["aria-live"]: props.$hasErrors ? "assertive" : "polite",
["data-has-errors"]: props.$hasErrors ? "true" : "false",
}))
.variants<NotificationProps, { $severity: "idle" | "error" }>({
base: `
inline-flex items-center gap-2 rounded-full px-4 py-1.5 text-sm
border transition-colors duration-150
`,
variants: {
$severity: {
idle: "border-slate-300 bg-white text-slate-900",
error: "border-red-500 bg-red-50 text-red-900",
},
},
})
Chaining keeps concerns focused: derive your computed values in the first handler, attach DOM attributes in the second, etc. The logic stack executes in order, so later handlers can override anything from earlier ones if necessary.
$ prefixes for props that should never reach the DOM.rc.extend or useClassmate to reuse the same derived data in different contexts.