Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
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:
120,289
Snippets:
1,264
Trust Score:
9.5
Update:
19 hours ago
Context
Skills
Chat
Benchmark
90.7
Suggestions
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 full-featured admin interfaces powered by [ra-core](https://marmelab.com/ra-core/), [shadcn/ui](https://ui.shadcn.com/), and [Tailwind CSS](https://tailwindcss.com/). It provides a complete set of CRUD pages (List, Show, Edit, Create), a data table with sorting, filtering, bulk actions, and column selection, form inputs with validation and data binding, reference field support for relational data, authentication, i18n, light/dark theming, and responsive layout — all compatible with any REST, GraphQL, or custom API via ra-core's data provider abstraction. The library is organized around the `<Admin>` root component, which wires together a data provider, an optional auth provider, i18n, routing, and theming. Resources declared as `<Resource>` children define the CRUD routes and map to API endpoints. Developers compose pages from field components (`TextField`, `ReferenceField`, `DateField`, etc.), input components (`TextInput`, `SelectInput`, `AutocompleteInput`, etc.), layout components (`List`, `Edit`, `Create`, `Show`, `SimpleForm`, `DataTable`), and action buttons (`CreateButton`, `EditButton`, `DeleteButton`, `ExportButton`, etc.). All components are exported from a single barrel at `@/components/admin`. --- ## `<Admin>` — Root Application Component The entry point of a shadcn-admin-kit application. Wraps `ra-core`'s context and UI layers together with theming and default login/layout/not-found pages. Accepts a `dataProvider` (required), optional `authProvider`, `i18nProvider`, `dashboard`, custom `layout`, and routing options. ```tsx import { Admin } from "@/components/admin/admin"; import { Resource } from "ra-core"; import simpleRestProvider from "ra-data-simple-rest"; import { authProvider } from "./authProvider"; import { PostList } from "./posts/PostList"; import { PostEdit } from "./posts/PostEdit"; import { PostCreate } from "./posts/PostCreate"; import { Dashboard } from "./Dashboard"; const dataProvider = simpleRestProvider("https://api.example.com"); export const App = () => ( <Admin dataProvider={dataProvider} authProvider={authProvider} dashboard={Dashboard} title="My Admin" disableTelemetry > <Resource name="posts" list={PostList} edit={PostEdit} create={PostCreate} /> <Resource name="categories" list={CategoryList} /> </Admin> ); ``` --- ## `<List>` — List Page A complete list page component that fetches records, manages filters, displays pagination, and renders a breadcrumb and title. Wraps `ra-core`'s `ListBase`. Children are typically a `<DataTable>`. Supports `filters`, `sort`, `perPage`, `exporter`, and `actions` props. ```tsx import { List } from "@/components/admin/list"; import { DataTable } from "@/components/admin/data-table"; import { TextInput } from "@/components/admin/text-input"; import { ReferenceInput } from "@/components/admin/reference-input"; import { AutocompleteInput } from "@/components/admin/autocomplete-input"; import { EditButton } from "@/components/admin/edit-button"; import { ReferenceField } from "@/components/admin/reference-field"; const filters = [ <TextInput source="q" placeholder="Search..." label={false} />, <ReferenceInput source="category_id" reference="categories" sort={{ field: "name", order: "ASC" }}> <AutocompleteInput placeholder="Filter by category" label={false} /> </ReferenceInput>, ]; export const PostList = () => ( <List filters={filters} sort={{ field: "created_at", order: "DESC" }} perPage={25} > <DataTable> <DataTable.Col source="id" /> <DataTable.Col source="title" /> <DataTable.Col label="Category"> <ReferenceField source="category_id" reference="categories" link="show" /> </DataTable.Col> <DataTable.Col source="published_at" /> <DataTable.Col> <EditButton /> </DataTable.Col> </DataTable> </List> ); ``` --- ## `<DataTable>` — Data Table Component Renders records in a sortable, selectable table. Uses `DataTable.Col` to define columns, supports bulk action buttons, row click navigation, and column visibility controls via `DataTable.NumberCol` for right-aligned numeric columns. ```tsx import { List } from "@/components/admin/list"; import { DataTable } from "@/components/admin/data-table"; import { ReferenceField } from "@/components/admin/reference-field"; import { BulkDeleteButton } from "@/components/admin/bulk-delete-button"; import { EditButton } from "@/components/admin/edit-button"; import { ShowButton } from "@/components/admin/show-button"; export const OrderList = () => ( <List> <DataTable rowClick="edit" bulkActionButtons={<BulkDeleteButton />} rowClassName={(record) => record.status === "cancelled" ? "opacity-50" : undefined } > <DataTable.Col source="id" disableSort /> <DataTable.Col source="reference" /> <DataTable.Col label="Customer"> <ReferenceField source="customer_id" reference="customers" /> </DataTable.Col> <DataTable.NumberCol source="total" options={{ style: "currency", currency: "USD" }} /> <DataTable.Col source="status" /> <DataTable.Col label="Actions"> <ShowButton /> <EditButton /> </DataTable.Col> </DataTable> </List> ); ``` --- ## `<Edit>` — Edit Page A complete record edit page that handles data fetching, breadcrumb navigation, page title, and default Show + Delete action buttons. Wrap form components inside a `<SimpleForm>` child. ```tsx import { Edit } from "@/components/admin/edit"; import { SimpleForm } from "@/components/admin/simple-form"; import { TextInput } from "@/components/admin/text-input"; import { BooleanInput } from "@/components/admin/boolean-input"; import { SelectInput } from "@/components/admin/select-input"; import { ReferenceInput } from "@/components/admin/reference-input"; import { required } from "ra-core"; export const CustomerEdit = () => ( <Edit> <SimpleForm> <TextInput source="first_name" validate={required()} /> <TextInput source="last_name" validate={required()} /> <TextInput source="email" type="email" validate={required()} /> <SelectInput source="status" choices={[ { id: "active", name: "Active" }, { id: "inactive", name: "Inactive" }, ]} /> <ReferenceInput source="group_id" reference="groups" /> <BooleanInput source="has_newsletter" /> <TextInput source="notes" multiline rows={4} /> </SimpleForm> </Edit> ); ``` --- ## `<Create>` — Create Page A complete new-record creation page. Wraps `ra-core`'s `CreateBase` with breadcrumb, title, and layout. Pair with `<SimpleForm>` to define form fields. ```tsx import { Create } from "@/components/admin/create"; import { SimpleForm } from "@/components/admin/simple-form"; import { TextInput } from "@/components/admin/text-input"; import { ReferenceInput } from "@/components/admin/reference-input"; import { DateInput } from "@/components/admin/date-input"; import { required } from "ra-core"; export const PostCreate = () => ( <Create> <SimpleForm> <TextInput source="title" validate={required()} /> <TextInput source="body" multiline rows={6} validate={required()} /> <ReferenceInput source="author_id" reference="authors" /> <DateInput source="publish_date" /> </SimpleForm> </Create> ); ``` --- ## `<Show>` — Show Page A complete record detail page. Fetches the record by `id` and renders breadcrumb, title, and an Edit button action. Use `<RecordField>` inside to display individual fields with labels. ```tsx import { Show } from "@/components/admin/show"; import { RecordField } from "@/components/admin/record-field"; import { ReferenceField } from "@/components/admin/reference-field"; import { NumberField } from "@/components/admin/number-field"; import { DateField } from "@/components/admin/date-field"; import { BooleanField } from "@/components/admin/boolean-field"; export const ProductShow = () => ( <Show> <div className="flex flex-col gap-4 max-w-lg"> <RecordField source="reference" /> <RecordField source="name" /> <RecordField source="category_id" label="Category"> <ReferenceField source="category_id" reference="categories" link="show" /> </RecordField> <RecordField source="price"> <NumberField source="price" options={{ style: "currency", currency: "USD" }} /> </RecordField> <RecordField source="available" > <BooleanField source="available" /> </RecordField> <RecordField source="created_at"> <DateField source="created_at" /> </RecordField> </div> </Show> ); ``` --- ## `<SimpleForm>` — Form Layout with Toolbar A vertical stacking form layout that wraps `ra-core`'s `Form`. Automatically renders a `<FormToolbar>` with Cancel and Save buttons unless a custom `toolbar` is provided. Use inside `<Edit>` or `<Create>`. ```tsx import { Edit } from "@/components/admin/edit"; import { SimpleForm, FormToolbar } from "@/components/admin/simple-form"; import { SaveButton } from "@/components/admin/form"; import { CancelButton } from "@/components/admin/cancel-button"; import { TextInput } from "@/components/admin/text-input"; const CustomToolbar = () => ( <FormToolbar> <CancelButton /> <SaveButton label="Publish" /> </FormToolbar> ); export const ArticleEdit = () => ( <Edit> <SimpleForm toolbar={<CustomToolbar />}> <TextInput source="title" /> <TextInput source="body" multiline rows={8} /> </SimpleForm> </Edit> ); ``` --- ## `<TextInput>` — Text Input Single-line or multiline text input bound to a form field. Supports validation, helper text, and all standard HTML `<input>` / `<textarea>` attributes via prop passthrough. ```tsx import { Edit, SimpleForm } from "@/components/admin"; import { TextInput } from "@/components/admin/text-input"; import { required, minLength, email } from "ra-core"; export const UserEdit = () => ( <Edit> <SimpleForm> <TextInput source="username" validate={[required(), minLength(3)]} /> <TextInput source="email" type="email" validate={[required(), email()]} /> <TextInput source="bio" multiline rows={4} helperText="Short description about the user" /> <TextInput source="website" type="url" placeholder="https://..." /> </SimpleForm> </Edit> ); ``` --- ## `<SelectInput>` — Select Dropdown Input A dropdown input for choosing a single value from a fixed list of choices or from a related resource via `<ReferenceInput>`. Supports creating new options on-the-fly with `create` or `onCreate`. ```tsx import { Edit, SimpleForm } from "@/components/admin"; import { SelectInput } from "@/components/admin/select-input"; import { ReferenceInput } from "@/components/admin/reference-input"; import { required } from "ra-core"; export const ProductEdit = () => ( <Edit> <SimpleForm> {/* Static choices */} <SelectInput source="status" validate={required()} choices={[ { id: "draft", name: "Draft" }, { id: "published", name: "Published" }, { id: "archived", name: "Archived" }, ]} /> {/* Choices from related resource */} <ReferenceInput source="category_id" reference="categories"> <SelectInput validate={required()} /> </ReferenceInput> </SimpleForm> </Edit> ); ``` --- ## `<AutocompleteInput>` — Searchable Autocomplete Input A popover-based searchable dropdown input. Works standalone with static `choices` or inside `<ReferenceInput>` for server-side filtering of related resources. Supports creating new options via `create` or `onCreate`. ```tsx import { Create, SimpleForm } from "@/components/admin"; import { AutocompleteInput } from "@/components/admin/autocomplete-input"; import { ReferenceInput } from "@/components/admin/reference-input"; export const InvoiceCreate = () => ( <Create> <SimpleForm> {/* Standalone with static choices */} <AutocompleteInput source="country" choices={[ { id: "us", name: "United States" }, { id: "fr", name: "France" }, { id: "de", name: "Germany" }, ]} placeholder="Select a country..." /> {/* With ReferenceInput for server-filtered results */} <ReferenceInput source="client_id" reference="clients"> <AutocompleteInput optionText={(record) => `${record.name} (${record.email})`} placeholder="Search clients..." /> </ReferenceInput> </SimpleForm> </Create> ); ``` --- ## `<BooleanInput>` — Toggle Switch Input A binary toggle switch input using shadcn's `<Switch>` component. Suitable for boolean flags like "is published", "is active", etc. Does not support `null` — use `<SelectInput>` for nullable booleans. ```tsx import { Edit, SimpleForm, TextInput } from "@/components/admin"; import { BooleanInput } from "@/components/admin/boolean-input"; import { required } from "ra-core"; export const PostEdit = () => ( <Edit> <SimpleForm> <TextInput source="title" validate={required()} /> <BooleanInput source="is_published" /> <BooleanInput source="allow_comments" /> <BooleanInput source="featured" defaultValue={false} /> </SimpleForm> </Edit> ); ``` --- ## `<ReferenceField>` — Foreign Key Display Field Fetches and displays a related record by following a foreign key. Renders the record's representation (configured via `ra-core`'s `recordRepresentation`) as a link to its show or edit page. Use inside `<DataTable.Col>` or `<RecordField>`. ```tsx import { List } from "@/components/admin/list"; import { DataTable } from "@/components/admin/data-table"; import { ReferenceField } from "@/components/admin/reference-field"; import { TextField } from "@/components/admin/text-field"; export const CommentList = () => ( <List> <DataTable> <DataTable.Col source="id" /> <DataTable.Col label="Post"> {/* Links to post's show page */} <ReferenceField source="post_id" reference="posts" link="show" /> </DataTable.Col> <DataTable.Col label="Author"> {/* Custom child for display */} <ReferenceField source="author_id" reference="users" link="edit"> <TextField source="username" /> </ReferenceField> </DataTable.Col> <DataTable.Col source="body" /> </DataTable> </List> ); ``` --- ## `<ReferenceArrayField>` — Array of Foreign Keys Display Field Fetches and displays multiple related records from an array of IDs. Renders them via `<SingleFieldList>` by default (as badges/chips). Use inside `<DataTable.Col>` or `<RecordField>` for many-to-many display. ```tsx import { List } from "@/components/admin/list"; import { DataTable } from "@/components/admin/data-table"; import { ReferenceArrayField } from "@/components/admin/reference-array-field"; import { SingleFieldList } from "@/components/admin/single-field-list"; import { BadgeField } from "@/components/admin/badge-field"; export const ArticleList = () => ( <List> <DataTable> <DataTable.Col source="title" /> <DataTable.Col source="tag_ids" label="Tags"> <ReferenceArrayField reference="tags" source="tag_ids"> <SingleFieldList> <BadgeField source="name" /> </SingleFieldList> </ReferenceArrayField> </DataTable.Col> </DataTable> </List> ); ``` --- ## `<ReferenceInput>` — Foreign Key Form Input A form input for editing many-to-one relationships. Fetches related records and provides them to a child input (defaults to `<AutocompleteInput>`). Use `source` for the foreign key field and `reference` for the related resource name. ```tsx import { Edit, SimpleForm, TextInput } from "@/components/admin"; import { ReferenceInput } from "@/components/admin/reference-input"; import { AutocompleteInput } from "@/components/admin/autocomplete-input"; import { SelectInput } from "@/components/admin/select-input"; export const ContactEdit = () => ( <Edit> <SimpleForm> <TextInput source="first_name" /> <TextInput source="last_name" /> {/* Default AutocompleteInput child */} <ReferenceInput source="company_id" reference="companies" /> {/* Custom SelectInput child */} <ReferenceInput source="category_id" reference="categories" sort={{ field: "name", order: "ASC" }} > <SelectInput optionText="label" /> </ReferenceInput> </SimpleForm> </Edit> ); ``` --- ## `<RecordField>` — Labeled Field for Show Pages Displays a labeled field-value pair in vertical (default) or inline layout. Renders a `<TextField>` by default; accepts `children`, a `field` component type, or a `render` function for custom display. Used inside `<Show>`. ```tsx import { Show } from "@/components/admin/show"; import { RecordField } from "@/components/admin/record-field"; import { NumberField } from "@/components/admin/number-field"; import { ReferenceField } from "@/components/admin/reference-field"; import { DateField } from "@/components/admin/date-field"; export const InvoiceShow = () => ( <Show> <div className="flex flex-col gap-4 max-w-lg"> <RecordField source="reference" variant="inline" /> <RecordField source="customer_id" label="Customer" variant="inline"> <ReferenceField source="customer_id" reference="customers" link="show" /> </RecordField> <RecordField source="amount" label="Total Amount" variant="inline" field={NumberField} /> <RecordField label="Issued At" variant="inline" render={(record) => new Date(record.issued_at).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", }) } /> </div> </Show> ); ``` --- ## `<AppSidebar>` — Navigation Sidebar The collapsible navigation sidebar included in the default `<Layout>`. Automatically renders links to the dashboard and all `<Resource>` list views. Collapses to icon-only on narrow viewports and draws as a mobile drawer. Extend or replace it for custom navigation items. ```tsx import { AppSidebar } from "@/components/admin/app-sidebar"; import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, } from "@/components/ui/sidebar"; import { Link } from "react-router"; import { Settings } from "lucide-react"; // Custom sidebar extending AppSidebar behavior export function CustomSidebar() { return ( // AppSidebar renders automatically inside the default Layout; // replace Layout's sidebar prop to use a custom one. <Sidebar variant="floating" collapsible="icon"> <SidebarHeader /> <SidebarContent> {/* AppSidebar renders resource menu items here by default */} <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton asChild> <Link to="/settings"> <Settings /> Settings </Link> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarContent> <SidebarFooter /> </Sidebar> ); } ``` --- ## `<ListGuesser>` / `<EditGuesser>` / `<ShowGuesser>` — Scaffolding Guessers Guessers automatically inspect API responses and generate appropriate CRUD components. Use them during development to scaffold code; they log generated component source to the browser console. ```tsx import { Admin } from "@/components/admin/admin"; import { ListGuesser } from "@/components/admin/list-guesser"; import { EditGuesser } from "@/components/admin/edit-guesser"; import { ShowGuesser } from "@/components/admin/show-guesser"; import { Resource } from "ra-core"; import simpleRestProvider from "ra-data-simple-rest"; // Rapid prototyping: guessers generate and log component code in dev mode export const App = () => ( <Admin dataProvider={simpleRestProvider("https://api.example.com")}> <Resource name="posts" list={ListGuesser} // logs: Guessed List: ... edit={EditGuesser} // logs: Guessed Edit: ... show={ShowGuesser} // logs: Guessed Show: ... /> </Admin> ); // Guessed List output example (copy from console): // import { DataTable } from "@/components/admin/data-table"; // import { List } from "@/components/admin/list"; // // export const PostList = () => ( // <List> // <DataTable> // <DataTable.Col source="id" /> // <DataTable.Col source="title" /> // <DataTable.Col source="author_id" /> // </DataTable> // </List> // ); ``` --- ## Authentication — `authProvider` + `<LoginPage>` + `<AuthCallback>` Pass an `authProvider` object to `<Admin>` to enable authentication. The built-in `<LoginPage>` is shown automatically when unauthenticated users access protected routes. `<AuthCallback>` handles OAuth redirect flows. ```tsx import { Admin } from "@/components/admin/admin"; import { Resource } from "ra-core"; import simpleRestProvider from "ra-data-simple-rest"; import type { AuthProvider } from "ra-core"; // Custom authProvider example const authProvider: AuthProvider = { login: async ({ email, password }) => { const response = await fetch("/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }), }); if (!response.ok) throw new Error("Invalid credentials"); const { token } = await response.json(); localStorage.setItem("auth_token", token); }, logout: async () => { localStorage.removeItem("auth_token"); }, checkAuth: async () => { if (!localStorage.getItem("auth_token")) { throw new Error("Not authenticated"); } }, checkError: async (error) => { if (error.status === 401 || error.status === 403) { localStorage.removeItem("auth_token"); throw new Error("Session expired"); } }, getIdentity: async () => { const token = localStorage.getItem("auth_token"); const response = await fetch("/api/auth/me", { headers: { Authorization: `Bearer ${token}` }, }); const { id, name, avatar } = await response.json(); return { id, fullName: name, avatar }; }, getPermissions: async () => [], }; export const App = () => ( <Admin dataProvider={simpleRestProvider("https://api.example.com")} authProvider={authProvider} requireAuth // redirect to login before rendering anything > <Resource name="posts" list={PostList} /> </Admin> ); ``` --- ## Theming — `<ThemeProvider>` + `<ThemeModeToggle>` `<Admin>` wraps the app in a `<ThemeProvider>` supporting `"light"`, `"dark"`, and `"system"` modes. The theme is persisted to ra-core's store (localStorage by default). Use `<ThemeModeToggle>` to expose a light/dark/system switcher in the UI. ```tsx import { Admin } from "@/components/admin/admin"; import { Layout } from "@/components/admin/layout"; import { ThemeModeToggle } from "@/components/admin/theme-mode-toggle"; import { UserMenu } from "@/components/admin/user-menu"; import { useTheme } from "@/components/admin/use-theme"; // Access theme programmatically const MyComponent = () => { const { theme, setTheme } = useTheme(); return ( <div> <p>Current theme: {theme}</p> <button onClick={() => setTheme("dark")}>Dark</button> <button onClick={() => setTheme("light")}>Light</button> <button onClick={() => setTheme("system")}>System</button> </div> ); }; // ThemeModeToggle as a standalone button in a custom top bar const CustomTopBar = () => ( <header className="flex items-center justify-end gap-2 p-2"> <ThemeModeToggle /> <UserMenu /> </header> ); export const App = () => ( <Admin dataProvider={dataProvider}> {/* ThemeProvider is built into Admin; no separate wrapper needed */} </Admin> ); ``` --- ## Custom Routes — `<CustomRoutes>` Register arbitrary React Router routes inside `<Admin>` using ra-core's `<CustomRoutes>`. These routes render inside the app layout but do not appear in the sidebar automatically — add sidebar links manually if needed. ```tsx import { Admin } from "@/components/admin/admin"; import { Resource, CustomRoutes } from "ra-core"; import { Route } from "react-router"; import simpleRestProvider from "ra-data-simple-rest"; const Settings = () => <div><h1>Settings</h1></div>; const Profile = () => <div><h1>My Profile</h1></div>; export const App = () => ( <Admin dataProvider={simpleRestProvider("https://api.example.com")}> <Resource name="posts" list={PostList} edit={PostEdit} /> <CustomRoutes> <Route path="/settings" element={<Settings />} /> <Route path="/profile" element={<Profile />} /> </CustomRoutes> </Admin> ); ``` --- ## Summary Shadcn Admin Kit is designed for building internal admin tools and back-office dashboards on top of any API. The primary use case is scaffolding full CRUD interfaces: starting with `<ListGuesser>` / `<EditGuesser>` / `<ShowGuesser>` to auto-generate component code from live API responses, then replacing guessers with hand-crafted `<List>` + `<DataTable>`, `<Edit>` + `<SimpleForm>`, and `<Show>` + `<RecordField>` components. Relational data is handled declaratively through `<ReferenceField>`, `<ReferenceArrayField>`, and `<ReferenceInput>`, which fetch related records automatically. Authentication, i18n, and theming are opt-in features that layer on top of the core CRUD workflow without requiring changes to individual resource components. Integration typically starts by installing a compatible `ra-core` data provider (50+ options available for REST, GraphQL, Firebase, Supabase, etc.) and wiring it to `<Admin>`. From there, components can be used in any React setup — Vite, Remix, Next.js — since the library only requires React, React Router, and TanStack Query as peer dependencies. The `@/components/admin` barrel export provides all public components from a single import path, and components are designed to be copied and customized (shadcn/ui pattern) rather than configured exclusively through props, making it straightforward to override styles, extend layouts, or replace individual pieces of the UI.