Usage Patterns
Practical ways to import, compose, and ship PyColors UI primitives in real SaaS interfaces without changing the public API.
Use primitives as product building blocksLink to section
PyColors UI primitives are intentionally small. They give you accessible, token-driven pieces for SaaS interfaces, but your application owns the product state, data fetching, routing, validation, and business rules.
Use this page when you know which components you need and want to compose them into a real screen without reading package source.
Core idea
Import public primitives from @pycolors/ui, keep product logic in your app,
and compose behavior at the page, feature, or pattern layer.
Try and copy the examplesLink to section
Complete UI installation first, including the Tailwind package scan and token CSS. Each Source tab below includes the complete file used by its Preview. Copy the file into your app; use the code-block copy button or select the code manually.
Dialog and Pagination use the optional lucide-react peer for their icons.
Install it if your app does not already provide it:
pnpm add lucide-reactThe four interactive examples keep state in memory. Refreshing resets them; no data is sent, stored remotely, or used to change an account. The form shows validation, the dialog commits or cancels an edit, the table changes pages, and the toast opens only after an action.
For example, save the form source as components/notification-settings.tsx and
render it from a Next.js page:
import { NotificationSettings } from "../components/notification-settings";
export default function SettingsPage() {
return <NotificationSettings />;
}The page can remain a Server Component. Each interactive example declares its
own client boundary and imports only React and the public @pycolors/ui entry
point. Connect your app's persistence and server-side validation separately.
Required usage rulesLink to section
These rules are part of the supported usage contract.
- Import from the public package entry point:
@pycolors/ui. - Keep
@pycolors/uiprimitives app-agnostic. Do not put auth, billing, routing, or product-specific decisions inside shared UI components. - Preserve native semantics: buttons for actions, links for navigation, labels for form controls, and table markup for tabular data.
- Add a client boundary only where your app uses browser-only behavior, local state, event handlers, or interactive Radix composition.
- Use existing component docs for exact props and variants before inventing a local wrapper.
- Keep accessibility responsibilities visible when composing overlays, forms, feedback, and dense data views.
Recommended defaultsLink to section
These defaults are not hard requirements, but they keep product surfaces consistent.
- Start with
Button,Input,Card,Alert, andTablebefore adding local abstractions. - Use
Dialogfor focused decisions and short forms. - Use
Sheetfor side workflows that should keep page context visible. - Use
Tabsfor switching between related panels, not for primary navigation. - Use
Toastfor non-blocking feedback after actions. - Use
Alertfor inline, persistent feedback that affects the current task. - Use
Paginationwhen the user needs stable page position and predictable record counts.
Import from the public entry pointLink to section
import {
Alert,
AlertDescription,
AlertTitle,
Button,
Checkbox,
CheckboxField,
CheckboxLabel,
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
Input,
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
Tabs,
TabsContent,
TabsList,
TabsTrigger,
Toast,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
} from "@pycolors/ui";Do not import from @pycolors/ui/src/* or @pycolors/ui/dist/*.
Those paths are internal package details and are not part of the public
contract.
Server and client component usageLink to section
Many PyColors primitives can be composed in a server-rendered page when you pass static content and no event handlers. The moment your file owns state, effects, or event handlers, move that composition to a small client component.
import { Alert, AlertDescription, AlertTitle, Button } from "@pycolors/ui";
export function BillingSummary() {
return (
<section className="space-y-4">
<Alert>
<AlertTitle>Plan active</AlertTitle>
<AlertDescription>
Your workspace is covered until the next renewal.
</AlertDescription>
</Alert>
<Button asChild>
<a href="/settings/billing">Manage billing</a>
</Button>
</section>
);
}Use a client boundary for event-driven composition.
Project: Website
Try renaming the project, submitting an empty name, and cancelling an edit. Escape dismisses the dialog and returns focus to its trigger. Saving changes only the displayed local name.
Client boundary rule
Make the smallest interactive component a client component. Keep surrounding pages and data loading server-side when possible.
Forms and settingsLink to section
Use forms when the user is editing product state. Keep labels, descriptions, and validation messages close to the control they explain.
Submit an empty or invalid email to see a connected field error, then enter
builder@example.com and toggle the weekly summary. Saving updates the status
message without navigating or making a network request.
Accessibility expectations:
- Every form control needs a visible label or equivalent accessible name.
- Validation errors should be text, not color alone.
- Disabled controls should still communicate why the action is unavailable when that reason matters to the workflow.
OverlaysLink to section
Choose overlays based on the job.
| Need | Primitive |
|---|---|
| Confirm or complete a focused task | Dialog |
| Work through a side panel without losing page context | Sheet |
| Pick a compact row or menu action | DropdownMenu |
"use client";
import {
Button,
Checkbox,
CheckboxField,
CheckboxLabel,
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@pycolors/ui";
export function FiltersSheet() {
return (
<Sheet>
<SheetTrigger asChild>
<Button variant="outline">Filters</Button>
</SheetTrigger>
<SheetContent side="right">
<SheetHeader>
<SheetTitle>Filter customers</SheetTitle>
<SheetDescription>
Narrow the table without leaving the current view.
</SheetDescription>
</SheetHeader>
<div className="mt-6 space-y-3">
<CheckboxField>
<Checkbox id="trialing" />
<CheckboxLabel htmlFor="trialing">Trialing</CheckboxLabel>
</CheckboxField>
<CheckboxField>
<Checkbox id="past-due" />
<CheckboxLabel htmlFor="past-due">Past due</CheckboxLabel>
</CheckboxField>
</div>
</SheetContent>
</Sheet>
);
}Overlay accessibility expectations:
- The trigger must have clear text or an accessible label.
- Dialog and Sheet titles should describe the task.
- Escape and focus-return behavior should remain intact.
- Do not place long multi-step workflows in a modal when a page would be clearer.
Product sections with TabsLink to section
Use Tabs to switch between related panels inside the same page.
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@pycolors/ui";
export function SettingsTabs() {
return (
<Tabs defaultValue="profile">
<TabsList aria-label="Settings sections">
<TabsTrigger value="profile">Profile</TabsTrigger>
<TabsTrigger value="billing">Billing</TabsTrigger>
<TabsTrigger value="security">Security</TabsTrigger>
</TabsList>
<TabsContent value="profile">Profile form</TabsContent>
<TabsContent value="billing">Billing controls</TabsContent>
<TabsContent value="security">Security settings</TabsContent>
</Tabs>
);
}Use navigation links instead of tabs when the destination is a different route, bookmarkable page, or primary information architecture.
Tables and paginationLink to section
Use Table for comparable records. Add Pagination when the result set is
split into stable pages.
| Name | Role |
|---|---|
| Ava Martin | Owner |
| Noah Chen | Member |
Page 1 of 3
Move through all three pages. Previous and Next are disabled at the boundaries;
the current page is announced and marked with aria-current="page". These
buttons paginate local example records. Use PaginationLink asChild with real
links when your app owns paginated routes.
Accessibility expectations:
- Keep column headers meaningful.
- Use captions or nearby headings when the table purpose is not obvious.
- Keep pagination controls keyboard-operable and labeled for the collection they control.
Feedback patternsLink to section
Use inline feedback when the message affects the current surface. Use toast feedback when the message confirms a completed action without blocking the user.
import { Alert, AlertDescription, AlertTitle } from "@pycolors/ui";
export function PaymentIssueAlert() {
return (
<Alert variant="destructive">
<AlertTitle>Payment failed</AlertTitle>
<AlertDescription>
Update your payment method to keep the workspace active.
</AlertDescription>
</Alert>
);
}Activate the button to simulate a successful save, dismiss the notification, and trigger it again. Dismissing returns focus to the trigger. It also closes after five seconds when not paused by interaction. In your app, open the toast after the mutation succeeds. Keep blocking failures in persistent inline feedback. This standalone example owns one provider and viewport; reuse your existing provider when integrating it.
Common mistakesLink to section
Prefer
- importing public primitives from
@pycolors/ui - using
Button asChildwhen a link should look like a button - keeping state, mutations, and routing in app code
- using
Alertfor persistent task feedback - using
Toastfor non-blocking action confirmation - using
Tableonly for structured, comparable records
Avoid
- importing from
@pycolors/ui/src/*or@pycolors/ui/dist/* - using a clickable
divinstead of a real button or link - putting billing, auth, or product rules inside UI primitives
- using a toast for a blocking error the user must fix now
- using tabs for primary route navigation
- hiding labels or validation behind color alone
Related docsLink to section
Next step
Start with the component docs for exact props, then use Patterns when the interaction needs state, async behavior, or repeated product decisions.