- AI
- Analytics
- Auth
- CMS
- Collaboration
- Cron Jobs
- Database
- Data Layer
- CRUD System
- Design System
- Transactional Emails
- Feature Flags
- Formatting
- Internationalization
- Observability
- Next.js Config
- Notifications
- Payments
- Security
- SEO
- Storage
- Testing
- Toolbar
- View System
- View Builder
- Trigger
- Trigger Rules
- Webhooks
CRUD UI
UI components for CRUD operations
CRUD UI Components
The UI package in @repo/crud
provides a collection of React components for building CRUD interfaces. These components are built on top of the CRUD Engine and provide a seamless integration with React.
Features
- Ready-to-Use Components: Components for lists, forms, and detail views
- Customizable: Easily customize the appearance and behavior of components
- Responsive: Works well on all screen sizes
- Accessible: Built with accessibility in mind
- Internationalization: Support for multiple languages
- Theme Support: Works with your application’s theme
- TypeScript Support: Full TypeScript support with generics
Installation
This package is part of the zopio
monorepo and is available to all applications in the workspace.
# If you're adding it to a new package in the monorepo
pnpm add @repo/crud
Basic Usage
Setting Up the Provider
Before using the UI components, you need to set up the CRUD provider in your application:
import { ui } from '@repo/crud';
import { engine } from '@repo/crud';
import { providers } from '@repo/data';
// Create a data provider
const dataProvider = providers.supabase.createProvider({
url: process.env.SUPABASE_URL,
key: process.env.SUPABASE_KEY
});
// Create a CRUD engine
const crudEngine = engine.createCrudEngine({
dataProvider,
enableAudit: true,
enablePermissions: true
});
// Wrap your application with the CRUD provider
function App() {
return (
<ui.CrudProvider engine={crudEngine}>
<YourApp />
</ui.CrudProvider>
);
}
Core Components
ResourceList
The ResourceList
component displays a list of resources with sorting, filtering, and pagination.
import { ui } from '@repo/crud';
function UserList() {
return (
<ui.ResourceList
resource="users"
columns={[
{ field: 'id', headerName: 'ID' },
{ field: 'name', headerName: 'Name' },
{ field: 'email', headerName: 'Email' },
{ field: 'role', headerName: 'Role' },
{
field: 'actions',
headerName: 'Actions',
renderCell: (row) => (
<div>
<button onClick={() => console.log('View', row)}>View</button>
<button onClick={() => console.log('Edit', row)}>Edit</button>
<button onClick={() => console.log('Delete', row)}>Delete</button>
</div>
)
}
]}
pagination={{ page: 1, perPage: 10 }}
sort={{ field: 'name', order: 'asc' }}
filter={{ role: 'admin' }}
onRowClick={(row) => console.log('Clicked row:', row)}
actions={['view', 'edit', 'delete']}
bulkActions={['delete']}
toolbar
search
/>
);
}
Props
interface ResourceListProps {
resource: string;
columns: Column[];
pagination?: {
page: number;
perPage: number;
};
sort?: {
field: string;
order: 'asc' | 'desc';
};
filter?: Record<string, any>;
onRowClick?: (row: any) => void;
actions?: ('view' | 'edit' | 'delete' | React.ReactNode)[];
bulkActions?: ('delete' | React.ReactNode)[];
toolbar?: boolean;
search?: boolean;
title?: string;
emptyMessage?: string;
loadingMessage?: string;
errorMessage?: string;
onView?: (id: string | number) => void;
onEdit?: (id: string | number) => void;
onDelete?: (id: string | number) => void;
onBulkDelete?: (ids: (string | number)[]) => void;
}
interface Column {
field: string;
headerName: string;
width?: number | string;
align?: 'left' | 'center' | 'right';
sortable?: boolean;
filterable?: boolean;
renderCell?: (row: any) => React.ReactNode;
valueGetter?: (row: any) => any;
valueFormatter?: (value: any) => string;
}
ResourceDetails
The ResourceDetails
component displays the details of a resource.
import { ui } from '@repo/crud';
function UserDetails({ id }) {
return (
<ui.ResourceDetails
resource="users"
id={id}
fields={[
{ field: 'id', label: 'ID' },
{ field: 'name', label: 'Name' },
{ field: 'email', label: 'Email' },
{ field: 'role', label: 'Role' },
{
field: 'createdAt',
label: 'Created At',
render: (value) => new Date(value).toLocaleString()
}
]}
actions={[
{
label: 'Edit',
onClick: () => console.log('Edit user')
},
{
label: 'Delete',
onClick: () => console.log('Delete user'),
color: 'error'
}
]}
title="User Details"
goBack={() => console.log('Go back')}
/>
);
}
Props
interface ResourceDetailsProps {
resource: string;
id: string | number;
fields: Field[];
actions?: Action[];
title?: string;
goBack?: () => void;
loadingMessage?: string;
errorMessage?: string;
}
interface Field {
field: string;
label: string;
render?: (value: any, record: any) => React.ReactNode;
}
interface Action {
label: string;
onClick: () => void;
color?: 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info';
icon?: React.ReactNode;
disabled?: boolean;
}
ResourceForm
The ResourceForm
component provides a form for creating or updating a resource.
import { ui } from '@repo/crud';
function UserForm({ id }) {
return (
<ui.ResourceForm
resource="users"
id={id} // Omit for create form
fields={[
{ field: 'name', label: 'Name', type: 'text', required: true },
{ field: 'email', label: 'Email', type: 'email', required: true },
{
field: 'role',
label: 'Role',
type: 'select',
options: [
{ value: 'user', label: 'User' },
{ value: 'admin', label: 'Admin' }
]
},
{
field: 'active',
label: 'Active',
type: 'checkbox'
}
]}
onSuccess={(data) => console.log('Form submitted:', data)}
onCancel={() => console.log('Form cancelled')}
title={id ? 'Edit User' : 'Create User'}
submitLabel={id ? 'Update' : 'Create'}
cancelLabel="Cancel"
/>
);
}
Props
interface ResourceFormProps {
resource: string;
id?: string | number;
fields: FormField[];
onSuccess?: (data: any) => void;
onCancel?: () => void;
title?: string;
submitLabel?: string;
cancelLabel?: string;
loadingMessage?: string;
errorMessage?: string;
defaultValues?: Record<string, any>;
}
interface FormField {
field: string;
label: string;
type: 'text' | 'email' | 'password' | 'number' | 'date' | 'datetime' | 'select' | 'checkbox' | 'radio' | 'textarea';
required?: boolean;
disabled?: boolean;
placeholder?: string;
helperText?: string;
options?: { value: string | number; label: string }[];
min?: number;
max?: number;
step?: number;
rows?: number;
validate?: (value: any) => string | undefined;
transform?: (value: any) => any;
}
ResourceCreate
The ResourceCreate
component provides a page for creating a new resource.
import { ui } from '@repo/crud';
function CreateUser() {
return (
<ui.ResourceCreate
resource="users"
fields={[
{ field: 'name', label: 'Name', type: 'text', required: true },
{ field: 'email', label: 'Email', type: 'email', required: true },
{
field: 'role',
label: 'Role',
type: 'select',
options: [
{ value: 'user', label: 'User' },
{ value: 'admin', label: 'Admin' }
]
}
]}
onSuccess={(data) => console.log('User created:', data)}
onCancel={() => console.log('Create cancelled')}
title="Create User"
goBack={() => console.log('Go back')}
/>
);
}
Props
interface ResourceCreateProps {
resource: string;
fields: FormField[];
onSuccess?: (data: any) => void;
onCancel?: () => void;
title?: string;
goBack?: () => void;
submitLabel?: string;
cancelLabel?: string;
defaultValues?: Record<string, any>;
}
ResourceEdit
The ResourceEdit
component provides a page for editing an existing resource.
import { ui } from '@repo/crud';
function EditUser({ id }) {
return (
<ui.ResourceEdit
resource="users"
id={id}
fields={[
{ field: 'name', label: 'Name', type: 'text', required: true },
{ field: 'email', label: 'Email', type: 'email', required: true },
{
field: 'role',
label: 'Role',
type: 'select',
options: [
{ value: 'user', label: 'User' },
{ value: 'admin', label: 'Admin' }
]
}
]}
onSuccess={(data) => console.log('User updated:', data)}
onCancel={() => console.log('Edit cancelled')}
title="Edit User"
goBack={() => console.log('Go back')}
/>
);
}
Props
interface ResourceEditProps {
resource: string;
id: string | number;
fields: FormField[];
onSuccess?: (data: any) => void;
onCancel?: () => void;
title?: string;
goBack?: () => void;
submitLabel?: string;
cancelLabel?: string;
}
Supporting Components
Pagination
The Pagination
component provides pagination controls.
import { ui } from '@repo/crud';
function PaginatedList() {
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(10);
const total = 100;
return (
<div>
{/* Your list content */}
<ui.Pagination
page={page}
perPage={perPage}
total={total}
onPageChange={setPage}
onPerPageChange={setPerPage}
perPageOptions={[5, 10, 25, 50]}
/>
</div>
);
}
Props
interface PaginationProps {
page: number;
perPage: number;
total: number;
onPageChange: (page: number) => void;
onPerPageChange?: (perPage: number) => void;
perPageOptions?: number[];
showFirstButton?: boolean;
showLastButton?: boolean;
showPageNumbers?: boolean;
maxPageButtons?: number;
}
Filter
The Filter
component provides filtering controls.
import { ui } from '@repo/crud';
function FilterableList() {
const [filter, setFilter] = useState({});
return (
<div>
<ui.Filter
fields={[
{ field: 'name', label: 'Name', type: 'text' },
{
field: 'role',
label: 'Role',
type: 'select',
options: [
{ value: 'user', label: 'User' },
{ value: 'admin', label: 'Admin' }
]
},
{
field: 'active',
label: 'Active',
type: 'boolean'
},
{
field: 'createdAt',
label: 'Created After',
type: 'date'
}
]}
filter={filter}
onChange={setFilter}
onReset={() => setFilter({})}
/>
{/* Your list content */}
</div>
);
}
Props
interface FilterProps {
fields: FilterField[];
filter: Record<string, any>;
onChange: (filter: Record<string, any>) => void;
onReset?: () => void;
}
interface FilterField {
field: string;
label: string;
type: 'text' | 'number' | 'date' | 'select' | 'boolean';
options?: { value: string | number; label: string }[];
}
Sort
The Sort
component provides sorting controls.
import { ui } from '@repo/crud';
function SortableList() {
const [sort, setSort] = useState({ field: 'name', order: 'asc' });
return (
<div>
<ui.Sort
fields={[
{ field: 'name', label: 'Name' },
{ field: 'email', label: 'Email' },
{ field: 'createdAt', label: 'Created At' }
]}
sort={sort}
onChange={setSort}
/>
{/* Your list content */}
</div>
);
}
Props
interface SortProps {
fields: SortField[];
sort: { field: string; order: 'asc' | 'desc' };
onChange: (sort: { field: string; order: 'asc' | 'desc' }) => void;
}
interface SortField {
field: string;
label: string;
}
Search
The Search
component provides search functionality.
import { ui } from '@repo/crud';
function SearchableList() {
const [search, setSearch] = useState('');
return (
<div>
<ui.Search
value={search}
onChange={setSearch}
placeholder="Search users..."
onClear={() => setSearch('')}
/>
{/* Your list content */}
</div>
);
}
Props
interface SearchProps {
value: string;
onChange: (value: string) => void;
placeholder?: string;
onClear?: () => void;
}
Layout Components
CrudLayout
The CrudLayout
component provides a layout for CRUD pages.
import { ui } from '@repo/crud';
function UserManagement() {
return (
<ui.CrudLayout
title="User Management"
actions={[
{
label: 'Create User',
onClick: () => console.log('Create user'),
primary: true
}
]}
breadcrumbs={[
{ label: 'Dashboard', href: '/' },
{ label: 'Users' }
]}
>
{/* Your content */}
</ui.CrudLayout>
);
}
Props
interface CrudLayoutProps {
title: string;
actions?: Action[];
breadcrumbs?: Breadcrumb[];
children: React.ReactNode;
}
interface Action {
label: string;
onClick: () => void;
primary?: boolean;
icon?: React.ReactNode;
disabled?: boolean;
}
interface Breadcrumb {
label: string;
href?: string;
}
Toolbar
The Toolbar
component provides a toolbar for CRUD pages.
import { ui } from '@repo/crud';
function ListToolbar() {
return (
<ui.Toolbar
title="Users"
actions={[
{
label: 'Create User',
onClick: () => console.log('Create user'),
primary: true
},
{
label: 'Export',
onClick: () => console.log('Export users')
}
]}
filter={<ui.Search value="" onChange={() => {}} />}
/>
);
}
Props
interface ToolbarProps {
title?: string;
actions?: Action[];
filter?: React.ReactNode;
}
Form Components
Form
The Form
component provides a base form component.
import { ui } from '@repo/crud';
function UserForm() {
const handleSubmit = (data) => {
console.log('Form submitted:', data);
};
return (
<ui.Form
onSubmit={handleSubmit}
defaultValues={{ role: 'user' }}
>
<ui.TextField name="name" label="Name" required />
<ui.TextField name="email" label="Email" type="email" required />
<ui.SelectField
name="role"
label="Role"
options={[
{ value: 'user', label: 'User' },
{ value: 'admin', label: 'Admin' }
]}
/>
<ui.CheckboxField name="active" label="Active" />
<ui.FormButtons
submitLabel="Save"
cancelLabel="Cancel"
onCancel={() => console.log('Cancelled')}
/>
</ui.Form>
);
}
Props
interface FormProps {
onSubmit: (data: any) => void;
defaultValues?: Record<string, any>;
children: React.ReactNode;
}
TextField
The TextField
component provides a text input field.
import { ui } from '@repo/crud';
function NameField() {
return (
<ui.TextField
name="name"
label="Name"
required
placeholder="Enter your name"
helperText="Your full name"
validate={(value) => {
if (!value) return 'Name is required';
if (value.length < 3) return 'Name must be at least 3 characters';
return undefined;
}}
/>
);
}
Props
interface TextFieldProps {
name: string;
label: string;
type?: 'text' | 'email' | 'password' | 'number' | 'date' | 'datetime';
required?: boolean;
disabled?: boolean;
placeholder?: string;
helperText?: string;
validate?: (value: any) => string | undefined;
min?: number;
max?: number;
step?: number;
}
SelectField
The SelectField
component provides a select input field.
import { ui } from '@repo/crud';
function RoleField() {
return (
<ui.SelectField
name="role"
label="Role"
options={[
{ value: 'user', label: 'User' },
{ value: 'admin', label: 'Admin' }
]}
required
helperText="Select a role"
/>
);
}
Props
interface SelectFieldProps {
name: string;
label: string;
options: { value: string | number; label: string }[];
required?: boolean;
disabled?: boolean;
helperText?: string;
validate?: (value: any) => string | undefined;
}
CheckboxField
The CheckboxField
component provides a checkbox input field.
import { ui } from '@repo/crud';
function ActiveField() {
return (
<ui.CheckboxField
name="active"
label="Active"
helperText="Is this user active?"
/>
);
}
Props
interface CheckboxFieldProps {
name: string;
label: string;
disabled?: boolean;
helperText?: string;
validate?: (value: any) => string | undefined;
}
RadioField
The RadioField
component provides a radio input field.
import { ui } from '@repo/crud';
function GenderField() {
return (
<ui.RadioField
name="gender"
label="Gender"
options={[
{ value: 'male', label: 'Male' },
{ value: 'female', label: 'Female' },
{ value: 'other', label: 'Other' }
]}
required
helperText="Select your gender"
/>
);
}
Props
interface RadioFieldProps {
name: string;
label: string;
options: { value: string | number; label: string }[];
required?: boolean;
disabled?: boolean;
helperText?: string;
validate?: (value: any) => string | undefined;
}
TextAreaField
The TextAreaField
component provides a textarea input field.
import { ui } from '@repo/crud';
function BioField() {
return (
<ui.TextAreaField
name="bio"
label="Bio"
rows={4}
placeholder="Tell us about yourself"
helperText="A short description about yourself"
/>
);
}
Props
interface TextAreaFieldProps {
name: string;
label: string;
rows?: number;
required?: boolean;
disabled?: boolean;
placeholder?: string;
helperText?: string;
validate?: (value: any) => string | undefined;
}
FormButtons
The FormButtons
component provides submit and cancel buttons for forms.
import { ui } from '@repo/crud';
function FormActions() {
return (
<ui.FormButtons
submitLabel="Save"
cancelLabel="Cancel"
onCancel={() => console.log('Cancelled')}
submitDisabled={false}
cancelDisabled={false}
/>
);
}
Props
interface FormButtonsProps {
submitLabel?: string;
cancelLabel?: string;
onCancel?: () => void;
submitDisabled?: boolean;
cancelDisabled?: boolean;
}
API Reference
For a complete API reference, please refer to the TypeScript definitions in the package.
- CRUD UI Components
- Features
- Installation
- Basic Usage
- Setting Up the Provider
- Core Components
- ResourceList
- Props
- ResourceDetails
- Props
- ResourceForm
- Props
- ResourceCreate
- Props
- ResourceEdit
- Props
- Supporting Components
- Pagination
- Props
- Filter
- Props
- Sort
- Props
- Search
- Props
- Layout Components
- CrudLayout
- Props
- Toolbar
- Props
- Form Components
- Form
- Props
- TextField
- Props
- SelectField
- Props
- CheckboxField
- Props
- RadioField
- Props
- TextAreaField
- Props
- FormButtons
- Props
- API Reference