Skip to main content
Zayne Labs UI uses a compound component pattern that provides flexibility and composability while maintaining a clean, predictable API.

Compound Component Pattern

Components are structured as a collection of sub-components that work together. Each component is namespaced and exported both individually and as a grouped namespace.

Example: Card Component

import { Card } from "@zayne-labs/ui-react/ui/card";

<Card.Root>
  <Card.Header>
    <Card.Title>Card Title</Card.Title>
    <Card.Description>Card description text</Card.Description>
  </Card.Header>
  <Card.Content>
    Main content goes here
  </Card.Content>
  <Card.Footer>
    <Card.Action>Action Button</Card.Action>
  </Card.Footer>
</Card.Root>
Each sub-component (Root, Header, Title, etc.) can be used independently, giving you full control over composition.

Data Attributes for Styling

Every component renders three data attributes for precise styling and debugging:
  • data-scope - The component family (e.g., "card", "carousel", "form")
  • data-part - The specific part within the component (e.g., "root", "header", "title")
  • data-slot - A combined identifier (e.g., "card-header", "carousel-button")

From the Card Source

card.tsx
export function CardRoot<TElement extends React.ElementType = "article">(
  props: PolymorphicProps<TElement, { asChild?: boolean; className?: string }>
) {
  const { as: Element = "article", asChild, className, ...restOfProps } = props;
  const Component = asChild ? Slot.Root : Element;

  return (
    <Component
      data-slot="card-root"
      data-scope="card"
      data-part="root"
      className={className}
      {...restOfProps}
    />
  );
}
These attributes enable:
  1. CSS targeting without relying on class names
  2. Style encapsulation - scope styles to specific components
  3. Developer experience - easy debugging in DevTools

Export Pattern

Components use a dual export pattern:
card-parts.ts
export {
  CardAction as Action,
  CardContent as Content,
  CardDescription as Description,
  CardFooter as Footer,
  CardHeader as Header,
  CardRoot as Root,
  CardTitle as Title,
} from "./card";
This allows both approaches:
// Namespaced (recommended)
import { Card } from "@zayne-labs/ui-react/ui/card";
<Card.Root>...</Card.Root>

// Individual imports
import { CardRoot, CardHeader } from "@zayne-labs/ui-react/ui/card";
<CardRoot>...</CardRoot>

UI Components vs Utility Components

Zayne Labs UI includes two types of components:

UI Components (ui/*)

Full-featured interactive components with state management:
  • Card - Presentational card layouts
  • Carousel - Image/content carousel with navigation
  • Form - Form field management with validation
  • DropZone - File upload areas
These components often include:
  • Context providers for state sharing
  • Event handlers and interactivity
  • Complex composition patterns

Utility Components (common/*)

Lightweight rendering utilities without styling:
  • For - Array rendering with type safety
  • Show - Conditional rendering helper
  • Slot - Component composition utility
  • ErrorBoundary - Error handling boundary
import { For } from "@zayne-labs/ui-react/common/for";
import { Show } from "@zayne-labs/ui-react/common/show";

<For each={items}>
  {(item, index) => (
    <Show.Root when={item.isActive}>
      <div>{item.name}</div>
    </Show.Root>
  )}
</For>
The Carousel demonstrates advanced compound component patterns:
carousel.tsx
<Carousel.Root images={images} hasAutoSlide>
  <Carousel.ItemList>
    {({ image, index }) => (
      <Carousel.Item key={index}>
        <img src={image.src} alt={image.alt} />
        <Carousel.Caption>{image.caption}</Carousel.Caption>
      </Carousel.Item>
    )}
  </Carousel.ItemList>
  
  <Carousel.Controls />
  
  <Carousel.IndicatorList>
    {({ index }) => <Carousel.Indicator currentIndex={index} />}
  </Carousel.IndicatorList>
</Carousel.Root>
Notice:
  • Render props for dynamic content (ItemList and IndicatorList)
  • Shared context - state managed in Root, consumed by children
  • Flexible composition - add/remove/reorder parts as needed

Key Principles

Composability

Components are building blocks. Mix and match parts to create custom layouts.

Flexibility

Use only what you need. Every part is optional and customizable.

Type Safety

Full TypeScript support with generic props and type inference.

Accessibility

Semantic HTML by default with polymorphic as prop support.

Next Steps

Styling

Learn how to style components with Tailwind or vanilla CSS

TypeScript

Explore type signatures and polymorphic props

Build docs developers (and LLMs) love