UI Components for Next.js Supabase SaaS Applications

Explore 60+ pre-built React components for building SaaS applications. Includes forms, data tables, navigation, and Makerkit-specific components built on Shadcn UI and Radix primitives.

Makerkit includes a comprehensive UI component library built on Shadcn UI and Radix Primitives. All components are fully accessible, customizable with Tailwind CSS, and designed specifically for SaaS applications.

Component architecture

Components are organized in the @kit/ui package and fall into two categories:

CategoryDescriptionExamples
Shadcn UIBase primitives from Shadcn UIButton, Input, Dialog, Select
MakerkitSaaS-specific componentsDataTable, MultiStepForm, Page layouts

All components are exported from @kit/ui with named exports:

import { Button, Input, Card } from '@kit/ui/button';
import { DataTable } from '@kit/ui/enhanced-data-table';
import { MultiStepForm } from '@kit/ui/multi-step-form';

Quick start

Here's a complete example showing common components working together:

'use client';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Button } from '@kit/ui/button';
import { Card, CardHeader, CardTitle, CardContent } from '@kit/ui/card';
import { Form, FormField, FormItem, FormLabel, FormControl } from '@kit/ui/form';
import { Input } from '@kit/ui/input';
const schema = z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Invalid email'),
});
export function ContactForm() {
const form = useForm({
resolver: zodResolver(schema),
defaultValues: { name: '', email: '' },
});
return (
<Card>
<CardHeader>
<CardTitle>Contact Us</CardTitle>
</CardHeader>
<CardContent>
<Form {...form}>
<form onSubmit={form.handleSubmit(console.log)} className="space-y-4">
<FormField
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input {...field} placeholder="Your name" />
</FormControl>
</FormItem>
)}
/>
<FormField
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input {...field} type="email" placeholder="you@example.com" />
</FormControl>
</FormItem>
)}
/>
<Button type="submit">Send Message</Button>
</form>
</Form>
</CardContent>
</Card>
);
}

Component categories

Form components

Build validated forms with React Hook Form integration.

ComponentDescriptionImport
FormForm wrapper with validation@kit/ui/form
InputText input field@kit/ui/input
TextareaMulti-line text input@kit/ui/textarea
SelectDropdown selection@kit/ui/select
CheckboxToggle checkbox@kit/ui/checkbox
SwitchToggle switch@kit/ui/switch
RadioGroupRadio button group@kit/ui/radio-group
InputOTPOne-time password input@kit/ui/input-otp
CalendarDate picker@kit/ui/calendar
SliderRange slider@kit/ui/slider

Data display

Present data in tables, lists, and cards.

ComponentDescriptionImport
DataTablePaginated, sortable tables@kit/ui/enhanced-data-table
TableBasic table structure@kit/ui/table
CardContent container@kit/ui/card
BadgeStatus indicators@kit/ui/badge
AvatarUser profile images@kit/ui/avatar
SkeletonLoading placeholders@kit/ui/skeleton
ChartData visualization@kit/ui/chart

Guide users through your application.

ComponentDescriptionImport
NavigationMenuMain navigation@kit/ui/navigation-menu
BreadcrumbPath navigation@kit/ui/breadcrumb
TabsTabbed interfaces@kit/ui/tabs
DropdownMenuAction menus@kit/ui/dropdown-menu
CommandCommand palette@kit/ui/command
SidebarApp sidebar@kit/ui/sidebar

Feedback

Communicate status and gather user input.

ComponentDescriptionImport
AlertStatus messages@kit/ui/alert
AlertDialogConfirmation dialogs@kit/ui/alert-dialog
DialogModal windows@kit/ui/dialog
SheetSide panels@kit/ui/sheet
PopoverFloating content@kit/ui/popover
TooltipHover hints@kit/ui/tooltip
ToastNotifications@kit/ui/sonner
ProgressProgress indicators@kit/ui/progress

Layout

Structure your pages consistently.

ComponentDescriptionImport
PagePage layouts@kit/ui/page
SeparatorVisual dividers@kit/ui/separator
ScrollAreaCustom scrollbars@kit/ui/scroll-area
CollapsibleExpandable sections@kit/ui/collapsible
AccordionStacked collapsibles@kit/ui/accordion

Makerkit components

These components are built specifically for SaaS applications:

Enhanced Data Table

A powerful table component with built-in pagination, sorting, and filtering:

import { DataTable } from '@kit/ui/enhanced-data-table';
function UsersTable({ users, pageCount }) {
const columns = [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'email', header: 'Email' },
{ accessorKey: 'role', header: 'Role' },
];
return (
<DataTable
columns={columns}
data={users}
pageIndex={0}
pageSize={10}
pageCount={pageCount}
/>
);
}

Multi-Step Form

Create wizard-style forms with step validation:

import { MultiStepForm, MultiStepFormStep, createStepSchema } from '@kit/ui/multi-step-form';
const FormSchema = createStepSchema({
account: z.object({
email: z.string().email(),
password: z.string().min(8),
}),
profile: z.object({
name: z.string().min(1),
company: z.string().optional(),
}),
});
function OnboardingWizard() {
const form = useForm({ resolver: zodResolver(FormSchema) });
return (
<MultiStepForm schema={FormSchema} form={form} onSubmit={handleSubmit}>
<MultiStepFormStep name="account">
<AccountFields />
</MultiStepFormStep>
<MultiStepFormStep name="profile">
<ProfileFields />
</MultiStepFormStep>
</MultiStepForm>
);
}

Empty State

Display helpful messages when there's no data:

import { EmptyState } from '@kit/ui/empty-state';
import { PlusIcon } from 'lucide-react';
function ProjectsEmpty() {
return (
<EmptyState
icon={<FolderIcon className="h-12 w-12" />}
title="No projects yet"
description="Create your first project to get started."
>
<Button>
<PlusIcon className="mr-2 h-4 w-4" />
Create Project
</Button>
</EmptyState>
);
}

Stepper

Show progress through multi-step processes:

import { Stepper } from '@kit/ui/stepper';
function CheckoutProgress({ currentStep }) {
return (
<Stepper
variant="numbers"
steps={['Cart', 'Shipping', 'Payment', 'Confirmation']}
currentStep={currentStep}
/>
);
}

Loading Overlay

Show loading state over content:

import { LoadingOverlay } from '@kit/ui/loading-overlay';
function DataPanel({ isLoading, children }) {
return (
<div className="relative">
{isLoading && <LoadingOverlay />}
{children}
</div>
);
}

GDPR-compliant cookie consent:

import { CookieBanner } from '@kit/ui/cookie-banner';
function Layout({ children }) {
return (
<>
{children}
<CookieBanner />
</>
);
}

Customization

Using Tailwind CSS

All components accept className props for customization:

<Button className="bg-gradient-to-r from-blue-500 to-purple-600">
Gradient Button
</Button>
<Card className="shadow-xl border-2 border-primary">
<CardContent>Custom styled card</CardContent>
</Card>

Component variants

Many components support variants for different styles:

// Button variants
<Button variant="default">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
// Alert variants
<Alert variant="default">Info</Alert>
<Alert variant="success">Success</Alert>
<Alert variant="warning">Warning</Alert>
<Alert variant="destructive">Error</Alert>
// Badge variants
<Badge variant="default">Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="outline">Outline</Badge>

Modifying component source

Components live in packages/ui/src/. You can modify them directly:

packages/ui/src/
├── shadcn/ # Shadcn UI components
│ ├── button.tsx
│ ├── input.tsx
│ └── ...
├── makerkit/ # Makerkit components
│ ├── enhanced-data-table.tsx
│ ├── multi-step-form.tsx
│ └── ...
└── index.ts # Exports

Accessibility

All components follow WAI-ARIA guidelines:

  • Keyboard navigation: Full keyboard support for all interactive elements
  • Screen readers: Proper ARIA labels and roles
  • Focus management: Visible focus indicators and logical tab order
  • Color contrast: WCAG 2.1 AA compliant color ratios

Example with proper accessibility:

<Dialog>
<DialogTrigger asChild>
<Button>Open Settings</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Account Settings</DialogTitle>
<DialogDescription>
Update your account preferences here.
</DialogDescription>
</DialogHeader>
{/* Form content */}
</DialogContent>
</Dialog>

Dark mode

Components automatically adapt to dark mode. Use the ModeToggle component to let users switch:

import { ModeToggle } from '@kit/ui/mode-toggle';
function Header() {
return (
<header className="flex justify-between items-center p-4">
<Logo />
<ModeToggle />
</header>
);
}

Best practices

1. Use semantic components

Choose the right component for the job:

// Good: Semantic button for actions
<Button onClick={handleSubmit}>Save Changes</Button>
// Avoid: Div with click handler
<div onClick={handleSubmit}>Save Changes</div>

2. Provide loading states

Always show feedback during async operations:

<Button disabled={isSubmitting}>
{isSubmitting ? (
<>
<Spinner className="mr-2 h-4 w-4" />
Saving...
</>
) : (
'Save Changes'
)}
</Button>

3. Handle empty states

Never show blank screens:

{projects.length > 0 ? (
<ProjectList projects={projects} />
) : (
<EmptyState
title="No projects"
description="Create your first project to get started."
/>
)}

4. Add data-test attributes

Enable E2E testing with selectors:

<Button data-test="submit-form">Submit</Button>
<Input data-test="email-input" />

Component documentation

Explore detailed documentation for each component category: