UI Components
SaaSForge Core ships ~32 shadcn/ui primitives plus dozens of higher-order components organized by domain. This guide is the map: where each component lives, how to edit it, and how to add new ones.
Where components live
src/components/
├── ui/ shadcn primitives (32 files: Button, Card, Dialog, Input, ...)
├── marketing/ Hero, CTA, integrations, generic marketing helpers
├── hero2/ Alternate hero layouts
├── features/ Features grid sections
├── pricing/ Pricing tables (multi-variant)
├── testimonials/ Testimonial carousels and grids
├── faq/ FAQ accordions
├── footer/, header/ Layout chrome
├── dashboard/ KPI cards, charts, activity widgets
├── data-table/ Reusable table with pagination, filters, bulk actions
├── activity-feed/ Per-entity activity timeline
├── attachments/ File upload + listing
├── comments/ Threaded comments component
├── tags/ Tag picker + chips
├── custom-fields/ Generic dynamic field renderer
├── rbac/ Permission gates and role-aware controls
├── presence/ "Who's here" presence indicators
├── command-palette/ Cmd-K palette
├── onboarding/ Multi-step onboarding wizard
├── sidebar/ App sidebar
├── theme/ next-themes provider + toggle
├── docs/, mdx/ In-product docs renderer
└── usage-meter.tsx Plan-usage meter
The shadcn primitives (src/components/ui/)
| File | Purpose |
|---|---|
accordion.tsx | Collapsible panels (FAQ, settings sections) |
alert-dialog.tsx | Destructive confirmations |
avatar.tsx | User and workspace avatars |
badge.tsx, status-badge.tsx, section-badge.tsx | Inline labels, status, section tags |
button.tsx | All button variants and sizes |
card.tsx | Container with header/content/footer slots |
carousel.tsx | Embla-based slider |
chart.tsx | Recharts wrapper used by dashboard widgets |
checkbox.tsx, select.tsx, input.tsx, label.tsx | Form controls |
command.tsx | Cmd-K command list (used by command-palette) |
confirm-dialog.tsx | Generic yes/no modal |
dialog.tsx, sheet.tsx, popover.tsx, tooltip.tsx | Overlays |
dropdown.tsx, dropdown-menu.tsx | Menus |
empty-state.tsx, loading-state.tsx, skeleton.tsx | Async-state placeholders |
heading.tsx | Typography wrapper |
message-bubble.tsx | Chat / notification bubble |
pagination.tsx | Numeric pagination control |
section.tsx | Page section with consistent padding |
sonner.tsx | Toast provider (Sonner) |
table.tsx | Low-level table primitives |
tabs.tsx | Tabs/tab-list/tab-trigger |
All primitives use class-variance-authority for variants and clsx for composition. They are owned source: edit them like any other file.
Editing an existing primitive
Open the file under src/components/ui/ and adjust either:
- Tailwind classes in the base template literal: visual-only changes.
cva()definitions: add/remove variants and sizes.
Example: add a new xl size to button.tsx:
const buttonVariants = cva(
"inline-flex items-center justify-center ...",
{
variants: {
size: {
default: "h-9 px-4 py-2",
sm: "h-8 px-3 text-xs",
lg: "h-10 px-8",
xl: "h-12 px-10 text-base", // new
icon: "size-9",
},
},
},
);
Then <Button size="xl"> is available everywhere with no other change.
Adding a brand-new primitive
The repo uses the standard shadcn CLI workflow. To add, for example, the slider component:
pnpm dlx shadcn@latest add slider
The generator drops the component into src/components/ui/slider.tsx using your existing Tailwind config and CSS variables. No further wiring needed.
components.json at the repo root holds the CLI config (component path, alias, base color). Don't edit it unless you change the project structure.
Higher-order components
Beyond primitives, three sets of components do most of the heavy lifting:
Marketing sections
The landing page is composed from sections under src/components/:
hero2/: hero variantsfeatures/: feature gridspricing/: multi-tier pricing tablestestimonials/: quotes carousel/gridfaq/: FAQ accordioncta/,integrations/,process/,readiness/,services/
Every section reads its content from a matching file in src/config/ui/ (e.g., pricing/Pricing2.tsx pulls from src/config/ui/pricing2.ts). See Content & Editing for the full mapping.
Dashboard widgets (src/components/dashboard/)
KPI cards, area/bar charts, recent-activity feeds. They wrap the chart.tsx primitive (Recharts) with sensible defaults and the project's color palette via CSS variables.
Data-table system (src/components/data-table/)
A reusable table built on @tanstack/react-table with:
- Column sorting, filtering, hide/show
- Row selection + bulk actions
- Pagination (server- or client-side)
- CSV import/export
- Saved views
- Soft-delete + restore
The Products module uses this stack end-to-end: copy that pattern when adding a new entity (see Adding Your Own Model).
Composing a new marketing section
If you need a one-off section that isn't in src/components/:
// src/components/cta/SimpleCTA.tsx
import { Section } from "@/components/ui/section";
import { Button } from "@/components/ui/button";
interface SimpleCTAProps {
title: string;
body: string;
buttonLabel: string;
buttonHref: string;
}
export function SimpleCTA({ title, body, buttonLabel, buttonHref }: SimpleCTAProps) {
return (
<Section className="text-center">
<h2 className="text-3xl font-semibold">{title}</h2>
<p className="mt-4 text-muted-foreground">{body}</p>
<Button asChild className="mt-8">
<a href={buttonHref}>{buttonLabel}</a>
</Button>
</Section>
);
}
Drop it on a page and pass props from a config file in src/config/ui/.
When to edit a component vs. when to edit config
| Want to change... | Edit |
|---|---|
| Hero headline, button label, FAQ Q&A | Config file in src/config/ui/ |
| Color of all primary buttons | CSS variable in src/app/globals.css |
| Layout/order of section content | Section component file |
| Add a new variant (e.g., button size) | cva() block in the primitive |
| Add a brand-new primitive | pnpm dlx shadcn@latest add ... |
For brand basics (name, emails, logo paths) see Customization. For full theme work see Theming.