Try Live
Add Docs
Rankings
Plans
Documentation
Install
Plans
Install
More...
More...
Documentation
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Shadcn Admin Kit
https://github.com/marmelab/shadcn-admin-kit
Admin
A component kit for building admin applications with shadcn/ui, offering features like CRUD, data
...
Tokens:
141,092
Snippets:
1,099
Trust Score:
9.5
Update:
3 weeks ago
Context
Skills
Chat
Benchmark
85.4
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Shadcn Admin Kit Shadcn Admin Kit is a React component library for building admin applications using shadcn/ui components. It provides a complete set of pre-built, accessible components for creating CRUD interfaces, data tables, forms, and authentication flows. Built on top of ra-core (React Admin's headless core), it combines the power of Radix UI primitives with Tailwind CSS styling to deliver production-ready admin panels. The kit integrates seamlessly with any backend API through configurable data providers, supports multiple authentication strategies, and includes built-in i18n support, light/dark mode theming, and responsive layouts. Key features include automatic form generation with guessers, sophisticated data tables with sorting/filtering/pagination, reference field handling for related data, and a complete validation system powered by React Hook Form. ## Admin Component The root component that initializes the admin application context, providing data fetching, authentication, i18n, and routing capabilities. ```tsx import { Admin } from "@/components/admin/admin"; import { Resource } from "ra-core"; import simpleRestProvider from "ra-data-simple-rest"; import { PostList } from "./posts/PostList"; import { PostEdit } from "./posts/PostEdit"; import { PostCreate } from "./posts/PostCreate"; import { authProvider } from "./authProvider"; const dataProvider = simpleRestProvider("https://api.example.com"); const Dashboard = () => ( <div className="p-4"> <h1 className="text-2xl font-bold">Welcome to Admin</h1> <p>Your dashboard content here</p> </div> ); export const App = () => ( <Admin dataProvider={dataProvider} authProvider={authProvider} dashboard={Dashboard} title="My Admin App" > <Resource name="posts" list={PostList} edit={PostEdit} create={PostCreate} recordRepresentation="title" /> <Resource name="users" list={UserList} recordRepresentation={(record) => `${record.first_name} ${record.last_name}`} /> </Admin> ); ``` ## List Component A complete list page component that fetches records from the data provider and renders them with breadcrumb navigation, title, action buttons, filters, and pagination. ```tsx import { List, DataTable, TextInput, ReferenceInput, AutocompleteInput } from "@/components/admin"; import { ReferenceField } from "@/components/admin/reference-field"; import { EditButton } from "@/components/admin/edit-button"; import { DateField } from "@/components/admin/date-field"; const filters = [ <TextInput source="q" placeholder="Search..." label={false} />, <ReferenceInput source="category_id" reference="categories"> <AutocompleteInput placeholder="Filter by category" label={false} /> </ReferenceInput>, ]; export const PostList = () => ( <List filters={filters} sort={{ field: "created_at", order: "DESC" }} perPage={25} > <DataTable rowClick="edit"> <DataTable.Col source="id" /> <DataTable.Col source="title" /> <DataTable.Col label="Author"> <ReferenceField source="author_id" reference="users" link="show" /> </DataTable.Col> <DataTable.Col source="created_at"> <DateField source="created_at" /> </DataTable.Col> <DataTable.NumberCol source="views" /> <DataTable.Col label="Actions"> <EditButton /> </DataTable.Col> </DataTable> </List> ); ``` ## DataTable Component A powerful data table with sorting, bulk selection, row clicks, column visibility controls, and customizable rendering for each cell. ```tsx import { List, DataTable } from "@/components/admin"; import { ReferenceField } from "@/components/admin/reference-field"; import { BulkDeleteButton } from "@/components/admin/bulk-delete-button"; import { BulkExportButton } from "@/components/admin/bulk-export-button"; const BulkActionButtons = () => ( <> <BulkExportButton /> <BulkDeleteButton /> </> ); export const ProductList = () => ( <List> <DataTable rowClick="show" bulkActionButtons={<BulkActionButtons />} rowClassName={(record) => record.stock === 0 ? "bg-red-50" : undefined} > <DataTable.Col source="reference" /> <DataTable.Col source="name" /> <DataTable.Col label="Category"> <ReferenceField source="category_id" reference="categories" /> </DataTable.Col> <DataTable.NumberCol source="price" options={{ style: "currency", currency: "USD" }} /> <DataTable.NumberCol source="stock" /> <DataTable.Col source="status" render={(record) => ( <span className={record.status === "active" ? "text-green-600" : "text-red-600"}> {record.status} </span> )} /> </DataTable> </List> ); ``` ## Create Component A complete create page with breadcrumb navigation, title, and form handling for creating new records. ```tsx import { Create, SimpleForm, TextInput, NumberInput, ReferenceInput, SelectInput } from "@/components/admin"; import { required, minLength, number, minValue } from "ra-core"; export const ProductCreate = () => ( <Create> <SimpleForm> <TextInput source="name" validate={[required(), minLength(3)]} /> <TextInput source="description" multiline rows={4} helperText="A detailed description of the product" /> <ReferenceInput source="category_id" reference="categories"> <SelectInput optionText="name" validate={required()} /> </ReferenceInput> <NumberInput source="price" validate={[required(), number(), minValue(0)]} step={0.01} min={0} /> <NumberInput source="stock" validate={[required(), number(), minValue(0)]} min={0} /> </SimpleForm> </Create> ); ``` ## Edit Component A complete edit page that fetches a single record and provides form handling for updating it, with default Show and Delete action buttons. ```tsx import { Edit, SimpleForm, TextInput, BooleanInput, DateInput, ReferenceInput, AutocompleteInput } from "@/components/admin"; import { required, email } from "ra-core"; export const CustomerEdit = () => ( <Edit> <SimpleForm> <TextInput source="first_name" validate={required()} /> <TextInput source="last_name" validate={required()} /> <TextInput source="email" validate={[required(), email()]} /> <TextInput source="phone" /> <DateInput source="birthday" /> <BooleanInput source="has_newsletter" /> <BooleanInput source="is_vip" /> <ReferenceInput source="segment_id" reference="segments"> <AutocompleteInput optionText="name" /> </ReferenceInput> <TextInput source="address" multiline rows={3} /> </SimpleForm> </Edit> ); ``` ## Show Component A complete show page for displaying record details with breadcrumb navigation and a default Edit action button. ```tsx import { Show, RecordField, NumberField, ReferenceField, DateField } from "@/components/admin"; export const OrderShow = () => ( <Show> <div className="flex flex-col gap-4 max-w-2xl"> <RecordField source="reference" /> <RecordField source="customer_id" label="Customer"> <ReferenceField source="customer_id" reference="customers" link="show" /> </RecordField> <RecordField source="date"> <DateField source="date" /> </RecordField> <RecordField source="total" render={(record) => new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(record.total) } /> <RecordField source="status" variant="inline" /> <RecordField source="delivered" variant="inline"> {(record) => record.delivered ? "Yes" : "No"} </RecordField> </div> </Show> ); ``` ## SimpleForm Component A form layout component that stacks inputs vertically and provides a default toolbar with Cancel and Save buttons. ```tsx import { Edit, SimpleForm, TextInput, SelectInput, ArrayInput, SimpleFormIterator, NumberInput } from "@/components/admin"; import { FormToolbar, SaveButton, CancelButton } from "@/components/admin/simple-form"; import { required } from "ra-core"; const CustomToolbar = () => ( <FormToolbar> <CancelButton /> <SaveButton label="Save & Continue" /> </FormToolbar> ); export const InvoiceEdit = () => ( <Edit> <SimpleForm toolbar={<CustomToolbar />}> <TextInput source="invoice_number" validate={required()} /> <SelectInput source="status" choices={[ { id: "draft", name: "Draft" }, { id: "sent", name: "Sent" }, { id: "paid", name: "Paid" }, ]} /> <ArrayInput source="items"> <SimpleFormIterator inline> <TextInput source="description" /> <NumberInput source="quantity" /> <NumberInput source="unit_price" step={0.01} /> </SimpleFormIterator> </ArrayInput> </SimpleForm> </Edit> ); ``` ## TextInput Component Single-line or multiline text input for string values, wrapping shadcn's Input or Textarea component. ```tsx import { Create, SimpleForm, TextInput } from "@/components/admin"; import { required, minLength, maxLength } from "ra-core"; export const ArticleCreate = () => ( <Create> <SimpleForm> <TextInput source="title" validate={[required(), minLength(5), maxLength(200)]} helperText="Enter a descriptive title" /> <TextInput source="slug" format={(value) => value?.toLowerCase().replace(/\s+/g, "-")} /> <TextInput source="excerpt" multiline rows={3} validate={maxLength(500)} /> <TextInput source="body" multiline rows={10} validate={required()} /> </SimpleForm> </Create> ); ``` ## SelectInput Component Dropdown select input for choosing a single value from a list of predefined options with support for creating new choices. ```tsx import { Edit, SimpleForm, SelectInput, ReferenceInput } from "@/components/admin"; import { required } from "ra-core"; export const TaskEdit = () => ( <Edit> <SimpleForm> <SelectInput source="priority" choices={[ { id: "low", name: "Low" }, { id: "medium", name: "Medium" }, { id: "high", name: "High" }, { id: "urgent", name: "Urgent" }, ]} validate={required()} /> <SelectInput source="status" choices={[ { id: "todo", name: "To Do" }, { id: "in_progress", name: "In Progress" }, { id: "review", name: "In Review" }, { id: "done", name: "Done" }, ]} /> <ReferenceInput source="assignee_id" reference="users"> <SelectInput optionText="name" emptyText="Unassigned" /> </ReferenceInput> </SimpleForm> </Edit> ); ``` ## AutocompleteInput Component A searchable dropdown input with autocompletion that fetches choices dynamically and supports creating new options. ```tsx import { Create, SimpleForm, AutocompleteInput, ReferenceInput } from "@/components/admin"; import { required } from "ra-core"; const CreateTagDialog = ({ onCancel, onCreate, filter }) => { const handleCreate = () => { onCreate({ id: filter, name: filter }); }; return ( <div className="p-4"> <p>Create tag "{filter}"?</p> <button onClick={handleCreate}>Create</button> <button onClick={onCancel}>Cancel</button> </div> ); }; export const PostCreate = () => ( <Create> <SimpleForm> <ReferenceInput source="author_id" reference="users"> <AutocompleteInput optionText={(record) => `${record.first_name} ${record.last_name}`} validate={required()} filterToQuery={(searchText) => ({ q: searchText })} /> </ReferenceInput> <AutocompleteInput source="category" choices={[ { id: "tech", name: "Technology" }, { id: "science", name: "Science" }, { id: "lifestyle", name: "Lifestyle" }, ]} /> <ReferenceInput source="tag_ids" reference="tags"> <AutocompleteInput create={<CreateTagDialog />} createLabel="Create new tag" /> </ReferenceInput> </SimpleForm> </Create> ); ``` ## ReferenceInput Component A wrapper input that fetches related records from a reference resource for editing foreign key relationships. ```tsx import { Edit, SimpleForm, TextInput, ReferenceInput, AutocompleteInput, SelectInput } from "@/components/admin"; import { required } from "ra-core"; export const ContactEdit = () => ( <Edit> <SimpleForm> <TextInput source="first_name" validate={required()} /> <TextInput source="last_name" validate={required()} /> <TextInput source="email" type="email" /> <ReferenceInput source="company_id" reference="companies" sort={{ field: "name", order: "ASC" }} > <AutocompleteInput optionText="name" validate={required()} filterToQuery={(search) => ({ name: search })} /> </ReferenceInput> <ReferenceInput source="department_id" reference="departments" filter={{ active: true }} > <SelectInput optionText="name" emptyText="No department" /> </ReferenceInput> </SimpleForm> </Edit> ); ``` ## ReferenceField Component Displays a field from a related record by following a foreign key relationship, with optional linking to the related record. ```tsx import { List, DataTable, ReferenceField, Show, RecordField } from "@/components/admin"; export const CommentList = () => ( <List> <DataTable> <DataTable.Col source="id" /> <DataTable.Col source="body" /> <DataTable.Col label="Post"> <ReferenceField source="post_id" reference="posts" link="show" empty="No post" /> </DataTable.Col> <DataTable.Col label="Author"> <ReferenceField source="author_id" reference="users" link={(record, reference) => `/users/${record.author_id}/show`} > {/* Custom rendering */} <span className="font-semibold"> {(referenceRecord) => referenceRecord?.name} </span> </ReferenceField> </DataTable.Col> </DataTable> </List> ); export const CommentShow = () => ( <Show> <div className="flex flex-col gap-4"> <RecordField source="body" /> <RecordField source="post_id" label="Related Post"> <ReferenceField source="post_id" reference="posts" link="show" /> </RecordField> </div> </Show> ); ``` ## BooleanInput Component A toggle switch for boolean (true/false) values using shadcn's Switch component. ```tsx import { Edit, SimpleForm, TextInput, BooleanInput } from "@/components/admin"; export const SettingsEdit = () => ( <Edit> <SimpleForm> <TextInput source="site_name" /> <BooleanInput source="maintenance_mode" helperText="Enable to put the site in maintenance mode" /> <BooleanInput source="allow_registration" /> <BooleanInput source="email_verification" /> <BooleanInput source="two_factor_auth" defaultValue={false} /> </SimpleForm> </Edit> ); ``` ## NumberInput Component Input component for numeric values with parsing, min/max constraints, and step increments. ```tsx import { Edit, SimpleForm, TextInput, NumberInput } from "@/components/admin"; import { required, number, minValue, maxValue } from "ra-core"; export const ProductEdit = () => ( <Edit> <SimpleForm> <TextInput source="name" validate={required()} /> <NumberInput source="price" validate={[required(), number(), minValue(0)]} step={0.01} min={0} helperText="Price in USD" /> <NumberInput source="stock" validate={[required(), number(), minValue(0)]} min={0} step={1} /> <NumberInput source="weight" step={0.1} min={0} helperText="Weight in kg" /> <NumberInput source="discount_percent" validate={[number(), minValue(0), maxValue(100)]} min={0} max={100} /> </SimpleForm> </Edit> ); ``` ## DateInput Component Date picker input for editing date values in "YYYY-MM-DD" format using the native browser date picker. ```tsx import { Edit, SimpleForm, TextInput, DateInput } from "@/components/admin"; import { required } from "ra-core"; export const EventEdit = () => ( <Edit> <SimpleForm> <TextInput source="title" validate={required()} /> <DateInput source="start_date" validate={required()} /> <DateInput source="end_date" validate={required()} /> <DateInput source="registration_deadline" helperText="Last date for registration" /> {/* Parse to return Date object instead of string */} <DateInput source="published_at" parse={(val) => new Date(val)} /> {/* Custom format for UTC dates */} <DateInput source="utc_date" format={(value) => new Date(value).toISOString().split("T")[0]} /> </SimpleForm> </Edit> ); ``` ## ArrayInput Component Creates a list of sub-forms for editing arrays of embedded data within a record. ```tsx import { Edit, SimpleForm, TextInput, NumberInput, ArrayInput, SimpleFormIterator, ReferenceInput, AutocompleteInput } from "@/components/admin"; import { required, number, minValue } from "ra-core"; export const OrderEdit = () => ( <Edit> <SimpleForm> <TextInput source="order_number" disabled /> <ArrayInput source="items" validate={required()}> <SimpleFormIterator inline> <ReferenceInput source="product_id" reference="products"> <AutocompleteInput optionText="name" validate={required()} /> </ReferenceInput> <NumberInput source="quantity" validate={[required(), number(), minValue(1)]} min={1} /> <NumberInput source="unit_price" validate={[required(), number(), minValue(0)]} step={0.01} /> </SimpleFormIterator> </ArrayInput> <ArrayInput source="notes"> <SimpleFormIterator> <TextInput source="text" multiline /> </SimpleFormIterator> </ArrayInput> </SimpleForm> </Edit> ); ``` ## DeleteButton Component A button that deletes the current record with undo capability, showing a notification and redirecting after deletion. ```tsx import { Edit, SimpleForm, TextInput } from "@/components/admin"; import { DeleteButton } from "@/components/admin/delete-button"; import { ShowButton } from "@/components/admin/show-button"; const PostEditActions = () => ( <div className="flex gap-2"> <ShowButton /> <DeleteButton redirect="list" successMessage="Post deleted successfully" mutationOptions={{ onSuccess: () => { console.log("Post deleted"); }, }} /> </div> ); export const PostEdit = () => ( <Edit actions={<PostEditActions />}> <SimpleForm> <TextInput source="title" /> <TextInput source="body" multiline /> </SimpleForm> </Edit> ); ``` ## RecordField Component Displays a labeled field-value pair with flexible rendering options, supporting vertical or inline layouts. ```tsx import { Show, RecordField, NumberField, DateField, ReferenceField } from "@/components/admin"; export const CustomerShow = () => ( <Show> <div className="grid grid-cols-2 gap-4"> <RecordField source="first_name" /> <RecordField source="last_name" /> <RecordField source="email" /> <RecordField source="phone" empty="N/A" /> <RecordField source="created_at" field={DateField} /> <RecordField source="total_spent" render={(record) => new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(record.total_spent) } /> <RecordField source="segment_id" label="Segment"> <ReferenceField source="segment_id" reference="segments" /> </RecordField> <RecordField source="is_vip" variant="inline"> {(record) => record.is_vip ? "VIP Customer" : "Regular"} </RecordField> </div> </Show> ); ``` ## LoginPage Component The default login page displayed when authentication is enabled and the user is not authenticated. ```tsx import { Admin, LoginPage } from "@/components/admin"; import { Resource } from "ra-core"; import simpleRestProvider from "ra-data-simple-rest"; // Custom auth provider const authProvider = { login: async ({ email, password }) => { const response = await fetch("/api/auth/login", { method: "POST", body: JSON.stringify({ email, password }), headers: { "Content-Type": "application/json" }, }); if (!response.ok) throw new Error("Invalid credentials"); const { token } = await response.json(); localStorage.setItem("token", token); }, logout: async () => { localStorage.removeItem("token"); }, checkAuth: async () => { if (!localStorage.getItem("token")) throw new Error("Not authenticated"); }, checkError: async (error) => { if (error.status === 401) throw new Error("Unauthorized"); }, getIdentity: async () => { const token = localStorage.getItem("token"); const response = await fetch("/api/auth/me", { headers: { Authorization: `Bearer ${token}` }, }); return response.json(); }, getPermissions: async () => { return localStorage.getItem("permissions"); }, }; export const App = () => ( <Admin dataProvider={simpleRestProvider("https://api.example.com")} authProvider={authProvider} loginPage={LoginPage} requireAuth > <Resource name="posts" list={PostList} /> </Admin> ); ``` ## Custom Routes Register custom routes alongside resource CRUD routes using react-router's Route component. ```tsx import { Admin, CustomRoutes } from "ra-core"; import { Route } from "react-router"; import simpleRestProvider from "ra-data-simple-rest"; const Settings = () => ( <div className="p-4"> <h1 className="text-2xl font-bold">Settings</h1> {/* Settings form */} </div> ); const Profile = () => ( <div className="p-4"> <h1 className="text-2xl font-bold">Profile</h1> {/* Profile content */} </div> ); const Analytics = () => ( <div className="p-4"> <h1 className="text-2xl font-bold">Analytics Dashboard</h1> {/* Charts and metrics */} </div> ); export const App = () => ( <Admin dataProvider={simpleRestProvider("https://api.example.com")}> <Resource name="posts" list={PostList} edit={PostEdit} /> <Resource name="users" list={UserList} /> <CustomRoutes> <Route path="/settings" element={<Settings />} /> <Route path="/profile" element={<Profile />} /> <Route path="/analytics" element={<Analytics />} /> </CustomRoutes> </Admin> ); ``` ## Summary Shadcn Admin Kit excels at building data-driven admin interfaces where you need to manage resources with CRUD operations. Common use cases include content management systems, e-commerce backends, customer relationship management tools, inventory systems, and any application requiring secure data management with role-based access. The component library handles complex scenarios like nested forms with ArrayInput, relational data with ReferenceInput/ReferenceField, bulk operations on data tables, and custom validation rules. Integration patterns typically involve configuring a data provider that adapts your API to ra-core's interface, setting up an auth provider for authentication flows, and composing pages from List/Create/Edit/Show components with the appropriate input and field components. The guessers (ListGuesser, EditGuesser, ShowGuesser) provide excellent starting points by auto-generating component code based on API responses. For advanced customization, you can create custom layouts, override the sidebar navigation, add dashboard widgets, or build entirely custom pages while still benefiting from the data fetching and authentication infrastructure.