Component Anatomy
Naming Conventions covers the files and folders of a component. This page covers the inside of the main component file: a suggested section order and where each piece lives.
Framework agnostic
Examples are in React (the baseline for our examples). Treat this as a suggestion, not a rule — keep the overall structure, and adapt the concrete syntax to your framework and project.
The three sections
- Props — a single, exported, explicitly typed
UiComponentNameProps: variant props, content, and the optionaloverrideClassesobject. Document each prop with JSDoc so it surfaces in IDE tooltips and Storybook controls. - Styles — the
tv()definition at module scope (defined once). Each element maps to a named slot; prop-driven style changes go in variants. - Component — read the props (with defaults where needed), get the slot functions from
classes(), and render. ForwardoverrideClasses?.rooton therootslot so the parent can position the component (see Self-contained Components).
Reference example (React)
tsx
// UiCard/UiCard.tsx
import { tv } from "tailwind-variants";
// 1. Props
export type UiCardProps = {
/** Card title. */
title: string;
/** Supporting text under the title. */
description: string;
/**
* Visual size.
* @default "default"
*/
size?: "default" | "compact";
/** Classes injected by the parent for positioning. */
overrideClasses?: {
root?: string;
};
};
// 2. Styles
const classes = tv({
slots: {
root: "flex flex-col",
titleElement: "heading-6",
descriptionElement: "body-m",
},
variants: {
size: {
default: { root: "gap-3" },
compact: { root: "gap-2" },
},
},
});
// 3. Component
const UiCard = ({
title,
description,
size = "default",
overrideClasses,
}: UiCardProps) => {
const { root, titleElement, descriptionElement } = classes({ size });
return (
<div className={root({ class: overrideClasses?.root })}>
<h3 className={titleElement()}>{title}</h3>
<p className={descriptionElement()}>{description}</p>
</div>
);
};
export default UiCard;TIP
Keep this structure consistent across components so any file is easy to scan.