agentsclimarketplace

Component architecture

Skill param087/saas-ui-skills/skills/component-architecture

Production-grade SaaS UI skills for AI coding agents (React + Tailwind + shadcn/ui) — Codex, Claude Code, Cursor, OpenCode. One npx command to install.

Install
npx -y skills add param087/saas-ui-skills --skill component-architecture

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when structuring React components in a SaaS app — composing shadcn/ui, cva variants, compound components, controlled vs uncontrolled state, and knowing when to abstract versus inline.

SKILL.md

3.6 KB, as published. Nobody here has run it

Component Architecture

Overview

shadcn/ui gives you owned components (copied into your repo, not imported from a package). That means architecture is your job: build a thin layer of app-specific components on top of the primitives, use cva for variants, and resist abstracting until a pattern repeats. Good structure keeps a SaaS UI consistent without a maze of props.

When to use

  • Deciding how to structure or compose UI components.
  • A component has grown a dozen boolean props.
  • You're copy-pasting the same markup across pages.

Variants with cva, not boolean soup

import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

const badge = cva(
  "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold",
  {
    variants: {
      variant: {
        default: "border-transparent bg-primary text-primary-foreground",
        secondary: "border-transparent bg-muted text-muted-foreground",
        success: "border-transparent bg-emerald-500/15 text-emerald-600",
        destructive: "border-transparent bg-destructive/15 text-destructive",
      },
    },
    defaultVariants: { variant: "default" },
  },
);

export interface BadgeProps
  extends React.HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badge> {}

export function Badge({ className, variant, ...props }: BadgeProps) {
  return <span className={cn(badge({ variant }), className)} {...props} />;
}

One variant prop scales better than isSuccess, isError, isMuted booleans that can contradict each other.

Compound components for related parts

Expose structure, not a config object, when parts vary:

<Card>
  <CardHeader>
    <CardTitle>Usage</CardTitle>
    <CardDescription>This billing period</CardDescription>
  </CardHeader>
  <CardContent>{children}</CardContent>
  <CardFooter><Button>Upgrade</Button></CardFooter>
</Card>

This beats <Card title="…" description="…" footer={<Button/>} /> — consumers compose freely.

asChild to compose without wrapper soup

Radix's asChild merges props onto your element instead of rendering an extra node — great for making a link look like a button:

<Button asChild>
  <Link href="/billing">Manage plan</Link>
</Button>

Controlled vs uncontrolled

  • Uncontrolled (component owns state) by default — simpler, less re-render.
  • Controlled only when the parent needs the value (e.g. a filter that syncs to the URL). Support both like Radix: optional value + onValueChange, with internal fallback state.

When to abstract

Follow the rule of three: inline the markup the first two times; extract a component on the third repetition, when the shared shape is clear. Premature abstractions calcify the wrong API.

File structure

components/
  ui/         # shadcn primitives (owned)
  <feature>/  # app components grouped by feature
lib/utils.ts  # cn() + helpers

Pitfalls

  • Boolean prop explosion instead of a variant enum.
  • Mega-components taking a config object — prefer composition/children.
  • Abstracting too early — one usage is not a pattern.
  • Editing shadcn primitives in place for app logic — wrap them instead, keep ui/ upgradeable.
  • cn() not used — conditional class strings collide instead of merging.

Hand-off

A clean component layer that forms-and-validation, data-tables, and the SaaS-flow skills build their screens from.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.